user3624075
user3624075

Reputation:

Ember.js: How do I determine if a property has really changed in an Observer?

Let's say I have Articles which are backed by Sources. Each article has one source.

Sources have associated HTML which will be rendered on screen.

I want this HTML to be rendered only if the source changed.

App.ArticleView = Ember.View.extend({
  didInsertElement: function() {
    this.addObserver('controller.source.id', function() {
      console.log(arguments);
      renderHTML();
    });
  });
});

This behaves exactly as stated in the addObserver documentation, "Note that the observers are triggered any time the value is set, regardless of whether it has actually changed. Your observer should be prepared to handle that."

If setting a controller.model of Article A with source 1 is followed by setting a controller.model of Article B with source 1, the observer will call the method but I want to prevent renderHTML() from happening.

The documentation mentions "Observer Methods" which I'm not sure how to put to use in this case. Its signature (function(sender, key, value, rev) { };) looks exactly like what I need, but in my tests the arguments to the observer method are always 0: (current view), 1: "controller.source.id".

How can I get the previous value of controller.source.id, so as to determine whether to renderHTML() or not?

Upvotes: 2

Views: 2205

Answers (1)

Kingpin2k
Kingpin2k

Reputation: 47367

Ember.set won't set the value if it's the same as the current value, so your observer won't fire unless the value changes.

Here's an example:

http://emberjs.jsbin.com/jiyesuzi/2/edit

And here's the code in Ember.set that does it (https://github.com/emberjs/ember.js/blob/master/packages_es6/ember-metal/lib/property_set.js#L73)

// only trigger a change if the value has changed
  if (value !== currentValue) {
    Ember.propertyWillChange(obj, keyName);
    if (MANDATORY_SETTER) {
      if ((currentValue === undefined && !(keyName in obj)) || !obj.propertyIsEnumerable(keyName)) {
        Ember.defineProperty(obj, keyName, null, value); // setup mandatory setter
      } else {
        meta.values[keyName] = value;
      }
    } else {
      obj[keyName] = value;
    }
    Ember.propertyDidChange(obj, keyName);
  }

Unfortunately for you're case, Ember considers changing a portion of the chain as changing the item, aka if controller.source changes, then the observer will fire. You'll need to track your id differently to avoid you're observer from firing. You can create a different observer that always sets the current id on a local property, and then it won't fire the update when the chain is broken.

Inside the controller

currentArticleId: null,

watchArticle: function(){
  this.set('currentArticleId', this.get('article.id'));
}.observes('article.id')

And then you would watch controller.currentArticleId

Upvotes: 1

Related Questions