Ember.computed.filter performance

In my ember app, I have a list of users that is filterable and sortable. In order to achieve the filtering and sorting, I am using an Ember.computed.filter and Ember.computed.sort computed properties that build on each other, like so (truncated for brevity):

Ember.ArrayController.extend({
    sortField: 'nickname',
    sortDir: 'asc',
    sortedUsers: Ember.computed.sort('filteredUsers', 'usersSorting'),

    usersSorting: Ember.computed('sortField', 'sortDir', function() {
        return [this.get('sortField') + ':' + this.get('sortDir')];
    },

    filteredUsers: Ember.computed.filter('model', function() {
        //[...matching logic here that uses filNickname, filAgeMin, ect...]
        return matchesFilters;
    }).property('model', 'filNickname', 'filAgeMin', 'filAgeMax' 'filGender')

});

My question is, when used like this, are the Ember.computed.filter and Ember.computed.sort computed properties supposed to return a brand new array each time they recompute, and therefore cause my entire {{each sortedUsers}} list to re-render, or is it only supposed to remove/add/rearrange array elements in the same array, so that only those individual list items are modified in the DOM?

The reason I ask is because in my initial testing, it seems like the entire list is definitely being re-rendered each time, and was wondering if maybe its due to how I’m implementing these properties. While I don’t know much on the subject, from glancing at Ember.computed.filter/sort’s internals, it looks like they use arrayComputed, which I thought was not supposed to create a new array each time.

A follow up question: This list will also most likely contain a relatively large number of users at some point, easily in the hundreds. Similar to how (I’m assuming) Ember.computed.filter works, is there a way to limit the number of items that are rendered, to create a paginated or “Click to load more” type infinite list, without resorting to using something like Array.slice, which again, would cause a new array to be created, and re-render the entire list?

2 Likes

@billdami from your post it looks like this is working for you? I’m having an issue filtering a sorted list/sorting a filtered list. I roughly followed your code sample and my list does not render. I can render just the sorted list, but not the filtered sorted list.

My template is

{{#each aircraft in sortedAircraft}}
   ...
{{/each}}

The controller that works is

// this works
App.SystemConfigAircraftController = Ember.ArrayController.extend({
	sortProperties: [ 'isLocked:desc', 'noseNumber:asc' ],
	sortedAircraft: Ember.computed.sort('model', 'sortProperties')
});

And the failing controller

App.SystemConfigAircraftController = Ember.ArrayController.extend({

	sortProperties: [ 'isLocked:desc', 'noseNumber:asc' ],

        // does not work
	sortedAircraft: Ember.computed.sort('filteredAircraft', 'sortProperties'),

	filteredAircraft: Ember.computed.filter('model', function (aircraft) {
		return aircraft.isActive;
	}).property('model'),
});

What am I missing here? Can I use a computed sort on multiple props combined with a filter?

Yeah, it works for me, my issue was more related to the performance (i.e. the entire list re-rendering when the filtering only removed or added only one item).

You could probably get rid of the .property('model') on your filteredAircraft property, the only reason I use that is because my filtering has a few other dependent keys besides the array itself, which must cause the filtered array to recompute when they change.

Also, are the items in your array just plain javascript objects, or are they Ember.Object’s? If they are the latter, you should be using return aircraft.get('isActive') instead of accessing the property directly (not sure if that’ll make a difference though.

One other thing I might try is adding an {{else}} clause to your {{each}} loop and render something like “No items found”, that way you can check and see if maybe your filter is just removing all items, and that is why nothing is being rendered. (I would also add a console.log(aircraft) inside your Ember.computed.filter function, to see if its actually being fired.

DOH! Rookie mistake! They’re Ember objects so this should have been obvious! Thanks!

I didnt read the Ember implementation, but i guess that Em.computed.sort moves all elements around in the resulting array each time it is recomputed (quick sort implementation?), and as far i remember, Ember uses numerical positions to keep track of changes in arrays (see EmberArray - 4.6 - Ember API Documentation).

Also, i dont see easy to add a limit to filter since it does not provide a way to retain scope variables between callback calls (with that, at least we could count how many has passed and start returning false when the limit is reached).