ember.js: Cannot observe changes to nested properties on array using @each

Adding an observer on a nested property of objects within a collection does not reliably fire. I’ve created a test case that could be added to ember-runtime/tests/mixins/array_test.js that illustrates this. Observing a nested property on the EachProxy itself appears to work fine. However, observing the property using @each.nest.isDone does not.

In the following test, count1 is incremented to 1, count2 is not:

test('modifying nested contents of an object in an array should call @each observers', function() {
  var ary = new TestArray([
      Em.Object.create({ nest: Em.Object.create({ isDone: false}) }),
      Em.Object.create({ nest: Em.Object.create({ isDone: false}) }),
      Em.Object.create({ nest: Em.Object.create({ isDone: false}) }),
      Em.Object.create({ nest: Em.Object.create({ isDone: false}) })
    ]);

  var get = Ember.get, set = Ember.set;
  var count1 = 0, count2 = 0;

  // Works
  var each = get(ary, '@each');
  Ember.addObserver(each, 'nest.isDone', function() { count1++; });

  // Doesn't work
  Ember.addObserver(ary, '@each.nest.isDone', function() { count2++; });

  count1 = count2 = 0;
  var item = ary.objectAt(2);
  get(item, 'nest').set('isDone', true);

  equal(count1, 1, '@each.nest.isDone should have notified - observing @each array');
  equal(count2, 1, '@each.nest.isDone should have notified - observing chain containing @each');
});

About this issue

  • Original URL
  • State: closed
  • Created 12 years ago
  • Comments: 75 (62 by maintainers)

Commits related to this issue

Most upvoted comments

You can just add new computed property, which will contains only very nested object, which you’re trying to observe. And @each will works correctly Use next method

  allTickets: function () {
    var sections = this.get('sections'),
      each = Ember.$.each,
      tickets = [];

    each(sections, function (i, section) {
      each(section.rows, function (j, row) {
        each(row.tickets, function (k, ticket) {
          tickets.addObject(ticket);
        })
      });
    });

    return tickets;
  }.property(),

  checkedCount: function () {
    ///custom logic
    return something;
  }.property('allTickets.@each.is_checked')

instead of

  checkedCount: function () {
    ///custom logic
    return something
  }.property('sections.@each.rows.@each.tickets.@each.is_checked')

This is the Ember issue I encounter most frequently lately, often because it is hidden behind something else, like a filter or sort CP macro where it’s easy to forget you effectively creating a dependent key that is an @each with nested properties.

Historically (pre-2.0), capitalized paths indicated a global (so in your case it would be observing window.A). This issue is talking about nested paths with @each, which your comment is not discussing.

Most (hopefully all) of the special casing of capitalized paths should be removed. We should test against 2.8.1. If it still isnt working on 2.8, please open a new issue with a twiddle/jsbin reproduction.

@rwwagner90 regarding observers, my take on it is that using observers in cases when you’re not interacting with non-ember code is a big code smell. So using observers to e.g. trigger a d3 graph rendering when your app data changes is totally legit, but using observers to set one ember property when another changes is almost always more easily accomplished with a different approach

This is so easy to miss in the guides. I believe it should issue a warning when a nested @each is found.