Ember.js: Observing all object properties

前端 未结 2 499
-上瘾入骨i
-上瘾入骨i 2021-01-11 22:00

I would like to observe all changes on an object properties.
In the following example, i would like to be notified by the personChanged observer if firstname or lastname

相关标签:
2条回答
  • 2021-01-11 22:37

    This is possible using an ObjectProxy in two flavours, depending on your requirements. Both approaches differ only in when and how many times is the observer called and both of them rely on Ember.keys.

    The HTML for both the solutions is the same.

    HTML

    <script type="text/x-handlebars" data-template-name="app">
        Name: {{App.MyObject.firstname}} {{App.MyObject.lastname}}
    
        <ul>
        {{#each App.List}}
            <li>{{this}}</li>
        {{/each}}
        </ul>
    </script>
    

    Solution 1

    JsFiddle: http://jsfiddle.net/2zxSq/

    Javascript

    App = Em.Application.create();
    
    App.List = [];
    
    App.MyObject = Em.ObjectProxy.create({
        // Your Original object, must be defined before 'init' is called, however.
        content: Em.Object.create({
            firstname: 'first',
            lastname:  'last'
        }),
    
    
        // These following two functions can be abstracted out to a Mixin
        init: function () {
            var self = this;
            Em.keys(this.get('content')).forEach(function (k) {
                Em.addObserver(self.get('content'), k, self, 'personChanged')
            });
        },
    
        // Manually removing the observers is necessary.
        willDestroy: function () {
            var self = this;
            Em.keys(this.get('content')).forEach(function (k) {
                Em.removeObserver(self.get('content'), k, self, 'personChanged');
            });
        },
    
        // The counter is for illustrative purpose only
        counter: 0,
        // This is the function which is called.
        personChanged: function () {
            // This function MUST be idempotent.
            this.incrementProperty('counter');
            App.List.pushObject(this.get('counter'));
            console.log('person changed');
        }
    });
    
    App.ApplicationView = Em.View.extend({
        templateName: 'app'
    });
    
    // Test driving the implementation.
    App.MyObject.set('firstname', 'second');
    App.MyObject.set('lastname',  'last-but-one');
    
    App.MyObject.setProperties({
        'firstname': 'third',
        'lastname' : 'last-but-two'
    });
    

    While initialising MyObject, all properties which already exist on the content object are observed, and the function personChanged is called each time any of the property changes. However, since the observers are fired eagerly [1], the function personChanged should be idempotent, which the function in example is not. The next solution fixes this by making the observer lazy.

    Solution 2

    JsFiddle: http://jsfiddle.net/2zxSq/1/

    Javascript

    App.MyObject = Em.ObjectProxy.create({
        content: Em.Object.create({
            firstname: 'first',
            lastname:  'last'
        }),
    
    
        init: function () {
            var self = this;
            Em.keys(this.get('content')).forEach(function (k) {
                Em.addObserver(self, k, self, 'personChanged')
            });
        },
    
        willDestroy: function () {
            var self = this;
            Em.keys(this.get('content')).forEach(function (k) {
                Em.removeObserver(self, k, self, 'personChanged');
            });
        },
    
        // Changes from here 
        counter: 0,
        _personChanged: function () {
            this.incrementProperty('counter');
            App.List.pushObject(this.get('counter'));
            console.log('person changed');
        },
    
        // The Actual function is called via Em.run.once
        personChanged: function () {
            Em.run.once(this, '_personChanged');
        }
    });
    

    The only change here is that the actual observer function is now called only at the end of the Ember Run loop, which might be the behaviour you are looking for.

    Other notes

    These solutions use ObjectProxy instead of defining the observers on the object itself to avoid setting spurious observers (on properties such as init, willDestroy, etc.) or an explicit list of properties to observe.

    This solution can be extended to start observing dynamic properties by overriding the setUnknownProperty on the proxy to add an observer every time a key is added to content. The willDestroy will remain the same.

    Reference

    [1] This might get changed soon thanks to Asyn Observers

    0 讨论(0)
  • 2021-01-11 22:49

    What you have there is right using inline observers, but you just have some syntax errors. However it is even simpler to do with the key word observes. So I have tweaked your example a little below.

    App.MyObject: Ember.Object.create({
        firstname: 'first',
        lastname: 'last',
        personChanged: function() {
            //firstname or lastname changed
        }.observes('firstname','lastname')
    });
    

    Note: I put quotes around the properties

    Source: http://emberjs.com/guides/object-model/observers/

    0 讨论(0)
提交回复
热议问题