Computed property dependent on collection.@each.property recalculates unnecessarily


#1

I am developing an application where one of its basic models are “trainings”. Trainings have a finalization time, and so, I created an App.clock elements that acts as a global clock for my application.

I use the difference between the finalization time and the current time in many ways. For countdowns by example. Also for determine if a training is finished or not (the finalizesAt attribute is in the past or not).

This clock fires a ‘tick’ every second. This means that each second lots of computed properties that depends on the current time are recalculated. I don’t think that there is any solution to this by itself, but there is other computed properties based on those computed properties that gets therefore recalculated every second. And the chain continues…

This is the computed property dependent on the clock:

App.Training = DS.Model.extend({
  // [...]
  isFinished: function(){
    return this.get('finalizesAt') < new Date();
  }.property('finalizesAt', 'App.clock.second'),

So far so good. The problem comes with this other computed property I have on the controller.

  unfinishedTrainings: function(){
    var trainings = this.get('trainings') || [];
    return trainings.filter(function(training){ return !training.get('isFinished'); });
  }.property('trainings.@each.isFinished'),

Even if the training gets updated its isFinished property each second, I was expecting this other computed property not to be updated unless any training changes its isFinished value from false to true, but it is computed every second, even if trainings.@each.isFinished is, by example, [true, true, false] always.

Since there is several computed properties that depends on others like a chain, I am sure that I am computing properties unnecessarily, even making useless updates in the DOM. I want to stop this updates to blubble up to higher levels.

What is the correct way to listen for changes in the values of the elements of an array and only compute the property then one changes its value?

I’m pretty new to ember, so maybe I am totally wrong and I should use observers o another approach. Client side MVC is still a odd world for me.


#2

Here is an example that should illustrate how to trigger the update one time http://jsfiddle.net/2YZvd/

Like you suggested, an observer should be used in this scenario.


#3

I understand the idea. Instead of make the isFinished property a computed property, you make it a regular attribute and you add an observer to update that attribute when the condition is fullfilled.

I agree that this should avoid the property o be computed every second and therefore, the other computed properties that depend on this will also be updated only when there is a real change.

And, since I know that once a training is finished I can’t become unfinished again, I can remove the observer forever.

I’ll do it that way, thanks.

But still, I think that trainings.@each.isFinished should not be fired if the none of the isFinished changes its value, even if the property is computed. After all, the result is the same.


#4

I agree with cibernox. I don’t see why ember triggers the computation function for CP when they are meant to be “cacheable” when they haven’t changed at all.

I made a test to check how ember propagates the changes to chained dependencies:

http://jsfiddle.net/a4wxbxaw/