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,
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;
},
});
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;
}
});
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:
or
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.
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')
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With