Javascript

Simple Collection’s sorting and filtering with Ember.js


Hello all! This time I would like to show you how to implement a simple sorting and filtering of a collection on a Ember.js application.

I want to highlight that sorting and filtering collections are functionalities very easy to accomplish with Ember.js

For this example, we are going to use Ember's starter kit. You
should download it on Ember's official website.

Setting up your application

This basic application will be a simple countries list. We will be able
to sort by alphabetical and population order, we are also going to implement basic filtering. We need to modify the starter kit files, for this you should have
already unzipped the downloaded file.

Now, we need to open the index.html file and edit the content for the two existing handlebars templates to look like this:

<script type="text/x-handlebars">
  <h2>World's Population</h2>

  {{outlet}}
</script>

<script type="text/x-handlebars" id="index">
</script>

Then, open the js/app.js file and make it look like this:

WorldPopulation = Ember.Application.create();

WorldPopulation.IndexRoute = Ember.Route.extend({});

WorldPopulation.Router.map(function() {});

After that, download and include Ember Data, here is the download link. Save that file under js/lib folder. Finally, add this line before the <script src="js/app.js"></script> line on index.html:

<script src="js/libs/ember-data.js"></script>

Great! Now we are ready to start coding!

Creating the model

Now is time to define our basic data structure for this example. As you
can imagine, we need to create a Model. Please add the following code
below the WorldPopulation = Ember.Application.create() line on
js/app.js:

WorldPopulation.Country = DS.Model.extend({
  name: DS.attr('string'),
  continent: DS.attr('string'),
  population: DS.attr('number'),

  nId: function() {
    return +this.get('id');
  }.property('id')
});

In this case, we are going to use fixture data to try out the
application. We need to add the following code below the previous lines:

WorldPopulation.Country.FIXTURES = [
  {
    id: 1,
    name: 'China',
    continent: 'Asia',
    population: 1355692576
  },
  {
    id: 2,
    name: 'India',
    continent: 'Asia',
    population: 1236344631
  },
  {
    id: 3,
    name: 'USA',
    continent: 'America',
    population: 318892103
  },
  {
    id: 4,
    name: 'Indonesia',
    continent: 'Asia',
    population: 253609643
  },
  {
    id: 5,
    name: 'Brazil',
    continent: 'America',
    population: 202656788
  },
  {
    id: 6,
    name: 'Pakistan',
    continent: 'Asia',
    population: 196174380
  },
  {
    id: 7,
    name: 'Nigeria',
    continent: 'Africa',
    population: 177155754
  },
  {
    id: 8,
    name: 'Bangladesh',
    continent: 'Asia',
    population: 166280712
  },
  {
    id: 9,
    name: 'Russia',
    continent: 'Europe',
    population: 142470272
  },
  {
    id: 10,
    name: 'Japan',
    continent: 'Asia',
    population: 127103388
  }
];

The last step is to setup the ApplicationAdapter to use Fixtures.
Just add this line below WorldPopulation =
Ember.Application.create()'
:

WorldPopulation.ApplicationAdapter = DS.FixtureAdapter.extend({});

Updating the Index Template

Let's update the index template, by adding the following code, we will
be able to show a table with all the countries defined in our fixtures,
open index.html file and edit the index template to look like this:

<script type="text/x-handlebars" id="index">
  {{input type='text' value=filter placeholder='Name or Continent'}}

  <br />
  <br />

  <table>
    <thead>
      <tr>
        <th {{action 'sortBy' 'nId'}}>ID</th>
        <th {{action 'sortBy' 'name'}}>Name</th>
        <th {{action 'sortBy' 'continent'}}>Continent</th>
        <th {{action 'sortBy' 'population'}}>Population</th>
      </tr>
    </thead>
    <tbody>
      {{#each filteredContent}}
        <tr>
          <td>{{id}}</td>
          <td>{{name}}</td>
          <td>{{continent}}</td>
          <td>{{population}}</td>
        </tr>
      {{/each}}
    </tbody>
  </table>
</script>

Setting up the Route

We need to setup our WorldPopulation.IndexRoute, in order to load the
model that we have just created. The route should look like this:

WorldPopulation.IndexRoute = Ember.Route.extend({
  model: function() {
    return this.store.findAll('country');
  }
});

Right now, if you open the index.html file on a browser, you should be
able to see a table with the information from our fixtures.

If you are wondering how is this possible if we have not created the
controller nor declared the index route in the Router; please remember
that Ember.js declares an index route by default. It also automatically generates an IndexController for us. Ember's magic.

Creating the controller

Obviously, we need to create our own IndexController, so we can
implement sorting and filtering.

Add this code, before the WorldPopulation.IndexRoute definition in
js/app.js file:

WorldPopulation.IndexController = Ember.ArrayController.extend({
  actions: {
    sortBy: function(property) {
      this.set('sortProperties', [property]);
      this.set('sortAscending', !this.get('sortAscending'));
    }
  }
});

There is our first version of our IndexController, where we are easily
implementing the sort functionality. Every time we click on any table
header, we are going to see how the rows from the table are sorted.

Implementing the filters

The last thing that I would like to show you, is how to implement a
basic filtering functionality. At the end, our IndexController should
look like this:

WorldPopulation.IndexController = Ember.ArrayController.extend({
  filter: '',

  filteredContent: function(){
    var filter = this.get('filter');
    var rx = new RegExp(filter, 'gi');
    var countries = this.get('arrangedContent');

    return countries.filter(function(country) {
      return country.get('name').match(rx) || country.get('continent').match(rx);
    });

  }.property('arrangedContent', 'filter'),

  actions: {
    sortBy: function(property) {
      this.set('sortProperties', [property]);
      this.set('sortAscending', !this.get('sortAscending'));
    }
  }
});

One of the easiest way to filter a collection is doing it using a
computed property, as you can see the filteredContent is bound to
the template so every time we press a key in the filter
input, it makes the filtering functionality and returns all the countries
that match with the provided filter. Thanks to @tikotzky for showing me the concept of arrangedContent.

With all of this, we are witnessing the self-updating templates from
Ember.js.

Live example

Ember Starter Kit

Conclusions

Ignoring the fixtures code, we are building a complete Ember.js
application with less than 60 lines of code. Pretty amazing!

It is very easy to implement this kind of functionalities with Ember.js. I just want to emphasize that the sortProperties and sortAscending properties do not belong to Ember.ArrayController object. All of this is possible thanks to the Ember.SortableMixin which is used by the Ember.ArrayController by default.

If you are wondering about mixins, they are a very nice way to extend
properties and behavior among Ember objects.

I hope you have enjoyed reading this material. I would like to tell you
more about Ember in upcoming posts.

See you all next time!

Development
Angular 2 Overview
Javascript
AngularJS Providers under the hood
Agile
React JS: Communication between components