Speeding up DOM Rendering when filtering

A common pattern in Ember apps is that you want to some kind of live filtering and show/hide elements based on whether they match that filter. This works great if you only have a few simple elements - what happens however when you filter elements that are computationally expensive to render?

Typically your component may look like the following:

import Ember from 'ember';

export default Ember.Component.extend({
  // String filter
  filter: '',
  // We observe the filter and our items
  filteredItems: Ember.computed('filter', 'items', function() {
      let items = this.get('items'),
          filter = this.get('filter'),
          rx;

      rx = new RegExp(filter, 'gi');

      return items.filter(item => {
        return item.get('fullName').match(rx);
    });
  })
});

and in your template you would render those items with

{{#each filteredItems as |item|}}
  {{item-component item=item}}
{{/each}}

This approach works great if you have relatively simple view logic for the individual items. If you however have nested components or simply a complex view that requires a lot of DOM re-rendering it may be worth switching to a different approach.

Instead of completely removing items from the DOM when they do not match your filter, you can simply set their state to hidden and hide them with CSS.

export default Ember.Component.extend({
  // String filter
  filter: '',
  // We observe the filter and our items
  filteredItems: Ember.computed('filter', 'items', function() {
      let items = this.get('items'),
          filter = this.get('filter'),
          rx;

      rx = new RegExp(filter, 'gi');

      items.filter(item => {
        let matchesName = item.get('fullName').match(rx);

        item.set('hidden', !matchesName);
    });
  })
});

In your template you then simply render all items at all times:

{{#each items as |item|}}
  {{item-component item=item}}
{{/each}}

but hide them in your item-component by binding the hidden attribute to a CSS class:

export default Ember.Component.extend({
  classNameBindings: ['item.hidden:hidden'],
});

This way you prevent Ember from having to recompute the entire DOM tree.