Computed properties used inside #each


#1

I’ve hit quite a serious performance issue in an app I’ve been building. In a nutshell, I have an array of complex objects. I loop through the array with #each and a series of nested components renders these complex objects. There are a lot of them…

I may modify the properties on one of these objects, add one, remove one or move one to a different location in the array. In each case, I’d expect Ember to have to re-render (or more specifically, re-process) the one that has changed and leave the rest as they were. But this is not what I am seeing. Instead every object is being re-processed. This is causing huge performance problems for me.

{{#each data as |item|}}
   {{my-component item=item}}
{{/each}}

To be more specific, the components are not all re-rendering. If, in my-component I have this {{item.label}} then that seems to be ok. But, if I have {{computedProperty}} and a function:

computedProperty : function()
{
   var item=this.get("item");
    return item.label;
}.property('item.label')

…then every time I add an item to the data array (using pushObject), this function gets fired to re-calculate the label for every item in the array, not just the new one. If I update the label property for a single item in the data array, then, as expected, this function only get fired for the one item that had its label changed.

The same is true if I do something like this:

computedProperty : null,
    
x : function()
{
   var item=this.get("item");
   This.set("computedProperty",item.label);
}.observes('item.label')

So my question is, what do I need to do differently to prevent these functions firing when not needed? In my case these functions so some pretty complex processing, so having that redone each time I make a change to a single object is killing the app.

By that way, I’m using Ember-CLI v1.13.1 for my app.


#2

What version of Ember?


#3

Ember-CLI 1.13.1 which uses Ember 1.13.3


#4

This twiddle demonstrates the issue: http://ember-twiddle.com/a37e6128cc07fdf3a202 Watch the logs and you’ll see getLabel is called for every item in the array each time an item is appended to the end of the array.


#5

It looks like this was resolved in 1.13.7 http://jsbin.com/heleke/edit?html,js,output


#6

http://guides.emberjs.com/v2.0.0/templates/displaying-a-list-of-items/#toc_specifying-keys

I believe this is what you’re looking for. Ember 1.13’s Glimmer engine does a react style rerender the world on changes. The way to optimize this is to specify key property so that everything get matched up again on rerender.


#7

Hi lightblade

Thanks for the suggestion. I’ve tried using keys and it makes no difference. I am using them in my app since the identity of each item can be easily established from a property which contains a unique id. This is preferable to the default key (@identity) which is used if you do not specify one. But for this example, it makes no difference, so I’ve left it out in order to keep the example as simple as possible.

From what I can see, Glimmer makes no difference to whether or not these functions get called. Where Glimmer helps is, If the result of the call is that the content is unchanged from what was previously rendered, then there is no re-draw of the the element to the DOM. A nice improvement for sure.

But this seems to be a bug in Ember (or a misunderstanding about what to expect). The .property on getLabel specifies that it should be re-run if item.label changes. When I add an item to the end of the array, the previous items do not change, so it should not be re-run, but it is. Why does the function think that the other items’s labels have changed? I think that is the crux of the question here…


#8

Indeed it does! Thank you jasonmit.

I’ll get my App updated to Ember 1.13.7 now. I think that ought to be possible with Ember-CLI until it supports Ember 2.0.