Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ember view is not updating after re-ordering array used to create child views

I have an activities view that contains an array of activities. This array gets sorted sometimes based on a calculated property(distanced_from_home) that the user can update. When the array is sorted I want the child views to be re-rendered by according to the new order. Here is my Template and View:

index.html

  <script type="text/x-handlebars" data-template-name="activities">
    <h1>Activities</h1>
    {{#each activities}}
      {{view App.ActivityView activityBinding="this"}}
    {{/each}}
  </script>

app.js

  App.ActivitiesView = Em.View.extend({
    templateName: 'activities',
    activities: (function() {
      var list;
      list = App.activityController.activities;
      console.log("Activities View");
      console.log(list);
      return list;
    }).property('App.activityController.activities', '[email protected]_to_home').cacheable()
  });

When I cause the distance_to_home property to change, the console.log output shows the list array is properly sorted, but the children views are not re-rendered in the new order. If I leave this view and then comeback to it (it is rendered again) then the correct order is shown.

What should I do to get the view to update automatically?

Thanks,

EDIT

This seems to be working here and my 'activities' computed function is definitely firing, but no re-ordering of the view... I also tried to use an observer, but the results are the same.

  App.ActivitiesView = Em.View.extend({
    templateName: 'activities',
    classNames: ['activities rounded shadow'],
    homeBinding: 'App.user.home',
    activities: [],
    homeChanged: (function() {
      console.log("homeChanged()");
      this.set('activities', App.activityController.activities.sort(this.compare_activities));
      return null;
    }).observes('home'),

    compare_activities: function(a, b) {
      var result;
      result = 0;
      if (App.user.home != null) {
        result = a.get('distance_to_home') - b.get('distance_to_home');
      }
      return result;
    },

  });

I also tried to set the 'activities' parameter to an empty array and then reset it to the ordered array to force it to re-render, but this has no effect.

  App.ActivitiesView = Em.View.extend({
    templateName: 'activities',
    classNames: ['activities rounded shadow'],
    homeBinding: 'App.user.home',
    activities: [],
    homeChanged: (function() {
      console.log("homeChanged()");
      this.set('activities', []);
      this.set('activities', App.activityController.activities.sort(this.compare_activities));
      return null;
    }).observes('home'),

    compare_activities: function(a, b) {
      var result;
      result = 0;
      if (App.user.home != null) {
        result = a.get('distance_to_home') - b.get('distance_to_home');
      }
      return result;
    },

  });

EDIT (the second)

So it seems that using Ember.copy finally solved this. Setting the array property to an empty array (or null) did not have any effect. Here's the View code that finally worked:

  App.ActivitiesView = Em.View.extend({
    templateName: 'activities',
    classNames: ['activities rounded shadow'],
    homeBinding: 'App.user.home',
    activities: [],
    homeChanged: (function() {
      var list;
      list = App.activityController.activities.sort(this.compare_activities);
      this.set('activities', Ember.copy(list), true);
      return null;
    }).observes('home'),
    compare_activities: function(a, b) {
      var result;
      result = 0;
      if (App.user.home != null) {
        result = a.get('distance_to_home') - b.get('distance_to_home');
      }
      return result;
    }
  });
like image 203
CHsurfer Avatar asked Mar 06 '12 22:03

CHsurfer


2 Answers

Does this help? http://jsfiddle.net/rNzcy/

Putting together this simple example, I was a little confused why the view update didn't "just work" when sorting the content array in the ArrayController. It seems there are 2 simple things you can do to trigger the view update. Either:

  • Set the content array to null, sort the original content, and then set the sorted content.

or

  • Clone the content array when sorting it, via Ember.copy()

I guess Ember detects when a completely new Array object is set, and that triggers all the expected binding/view updates. Otherwise, maybe it's doing some sort of caching and if the Array object does not change (i.e. it's sorted in place) then no bindings fire? Just guessing.

UPDATE: See discussion @ https://github.com/emberjs/ember.js/issues/575#issuecomment-4397658 which has an improved workaround using propertyWillChange() and propertyDidChange() (http://jsfiddle.net/rNzcy/4/) -- should be a lot more efficient then cloning a large array.

like image 136
Adam Murray Avatar answered Nov 01 '22 17:11

Adam Murray


I met the same issue. And I don't think observe array.@each or array.length is good practice. At last I found this http://jsfiddle.net/rNzcy/4/ i.e. call this.propertyDidChange('foo')

like image 3
tonyouyang Avatar answered Nov 01 '22 17:11

tonyouyang