Ember 1.13 Attrs call property change on all observers


#1

I have a component like:

App.TestCompComponent = Ember.Component.extend({
    title: 'Test Component',
    
    disablePastChanged: function(){
        alert('disable past changed');
    }.observes('disablePast').on('init'),
    
    prop1Changed: function(){
        alert('Prop1 changed');
    }.observes('prop1').on('init')
});

and handlebars template as:

{{#test-comp disablePast=disablePast prop1=prop1}}
{{/test-comp}}

Now when a set any property/attrs on this component from parent component like this.set(‘disablePast’, false), both observers are fired (essentially all the attrs observers are fired). Is that the right behavior? As far i am concerned it should have fired just the property/attrs which changed.

JS Bin here – Why It fires the observers on startup, those properties are not being set anywhere? When you click on the button and it sets only one property/attrs it fires observer twice for the properties.

What am i doing wrong here??

Or should we using new lifecycle hooks for components:

didInitAttrs
didReceiveAttrs
willRender

But with these hooks i dont know which property actually changed. It gives me the updated properties.

Thanks!


#2

The problem at the moment is that components always re-render when their attributes change, and you are using the {{test-comp}} syntax instead of <test-comp /> (which isn’t available yet) with the former having two-way binding by default (for backwards compatibility) and the latter having one-way binding by default.

So when you modify prop1, you’re actually modifying the attribute prop1, which causes the component to re-render and both of your observers to fire. To get around this, you need to initialize a property on component in the didReceiveAttrs() hook as follows:

this.set('compProp1', this.getAttr('prop1'));

compProp1 is just used as a name until we get angle-bracket components, thereafter you could use:

this.set('prop1', this.getAttr('prop1'));

Since with angle-bracket components you would need to refer to the attribute as attrs.prop1.

Now we can work with compProp1 without worrying about modifying the attributes. You could now put an observer on compProp1 and it will work as expected. Note that to persist this value outside of the component you should give an action to your component as follows:

{{test-comp disablePast=disablePast prop1=prop1 updateProp1=(action (mut prop1))}}

And then in your component you can update prop1 using:

this.getAttr('updateProp1')(this.get('compProp1'));