Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Emberjs Handlebars #each helper slow when bound to computed properties

Tags:

ember.js

I'm running into a performance issue when I render a list of items using the #each helper or a collection view bound to some computed properties of an Ember.ArrayController. Performance is fine with a small list of 10 - 20 items, but around 50 - 100 it starts to lag quite noticeably. Try checking off a few todos or clicking "Add Todo"

Example code is here: http://jsfiddle.net/Jonesy/ed3ZS/4/

I noticed that the childViews in the DOM get re-rendered with each change, which could very well be the intended behaviour at the moment, but I'd prefer to be able to just have a todo be removed from the DOM of unfinished todos list individually and appended to the bottom of the finished todos list, which would in theory be much less costly.

What I'm hoping to have answered is whether am I looking at a performance issue with Ember collection views, or is displaying a list populated from a computed property a bad idea, and if so, will I need to manually manage the todo model's location in the view layer as it changes from unfinished to finished and vice versa.

like image 640
Jonesy Avatar asked May 29 '12 06:05

Jonesy


People also ask

What are handlebars in Ember JS?

Ember uses the Handlebars templating library to power your app's user interface. Handlebars templates contain static HTML and dynamic content inside Handlebars expressions, which are invoked with double curly braces: {{}} . Dynamic content inside a Handlebars expression is rendered with data-binding.

What are handlebars in programming?

What is Handlebars? Handlebars is a simple templating language. It uses a template and an input object to generate HTML or other text formats. Handlebars templates look like regular text with embedded Handlebars expressions.

How does Handlebars js work?

Written in JavaScript, Handlebars. js is a compiler that takes any HTML and Handlebars expression and compiles them to a JavaScript function. This derived JavaScript function then takes one parameter, an object—your data—and it returns a string with the HTML and the object properties' values inserted into the HTML.


1 Answers

This is a side-effect of how {{#each}} (and CollectionView, which is what powers it) works.

Internally, CollectionView uses something called array observers. An array observer allows you to subscribe to mutations made to an array when they are done using Ember.Array's mutation methods (replace, pushObject, popObject, etc.) The API for array observers is described here.

What this means is that, if you push a new object into a collection view, it will insert render one new element in the DOM and leave the rest in place.

In the example you posted, however, the array is not being mutated--you're creating a brand new Array object every time a new item is added or removed. When the binding synchronizes, it replaces the old array with the new array. To {{#each}}, this is no different than removing all of the elements and then adding them back in.

The solution to the problem is to use a single array, instead of a computed property that returns a different array object each time it changes. You can see the Contacts app for an example of how to do this.

Obviously this is a very common pattern, and we'd like to add some kind of filtering that does the right thing by default to Ember.ArrayController down the road.

like image 174
Tom Dale Avatar answered Oct 24 '22 05:10

Tom Dale