Sorting in Ember

Problem You have a collection of Post model object displayed in a template and you want to be able to sort ascending or descending on one or more keys. Solution Create (or if you already have one: update) a PostsController that contains an action that do the actual sorting. This action called is when you click on an element in the template. For this element you create an Ember component that imlements a click event handler and delegates the event to the controller. The main reason for creating a component for the sort element is that you want to update the state of the element with an appropriate icon (or color or whatever) indicating which key is used for sorting and if sorting happened in an ascending or descending order. By passing the controller's sortProperties and sortAscending properties we're able to remember the state and update and look of the element accordingly.

by bazzel

HTML

<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/foundation/5.1.1/css/foundation.css">
<script src="http://builds.handlebarsjs.com.s3.amazonaws.com/handlebars-v1.3.0.js"></script>
<script src="http://builds.emberjs.com/tags/v1.5.0-beta.1/ember.js"></script>
<script src="http://builds.emberjs.com/tags/v1.0.0-beta.6/ember-data.js"></script>

<script type="text/x-handlebars">
  {{outlet}}
</script>

<script type="text/x-handlebars" data-template-name="posts">
<dl class="sub-nav">
  <dt>Sort:</dt>
  {{sorting-key title='Id'
                key='id'
                sortProperties=sortProperties
                sortAscending=sortAscending
                action='sort'}}
  {{sorting-key title='Title'
                key='title'
                sortProperties=sortProperties
                sortAscending=sortAscending
                action='sort'}}
  {{sorting-key title='Published'
                key='published'
                sortProperties=sortProperties
                sortAscending=sortAscending
                action='sort'}}
</dl>

<ul>
  {{#each}}
  <li>{{title}} ({{id}})
    {{#if published}}<span>X</span>{{/if}}
  </li>
  {{/each}}
</ul>
</script>

<script type="text/x-handlebars" id="components/sorting-key">
  <a {{action 'sort' 'title'}}>{{title}}</a>
</script>

SCSS

.sub-nav dd {
  a:after {
    content: " \25B4\25BE";
  }

  &.asc a:after {
    content: " \25B4";
  }

  &.desc a:after {
    content: " \25BE";
  }
}

CoffeeScript

App = Ember.Application.create()
App.Store = DS.Store.extend()
App.ApplicationAdapter = DS.FixtureAdapter.extend()

App.Post = DS.Model.extend
  title: DS.attr 'string'
  published: DS.attr 'boolean'

App.Post.FIXTURES = [
  id: 1
  title: 'Perfect, just perfect'
  published: yes
,
  id: 2
  title: 'It\'s about the future, isn\'t it?'
  published: no      
,
  id: 3
  title: 'Now that\'s a risk you\'ll have to take you\'re life depends on it'
  published: yes      
,
  id: 4
  title: 'Save the clock tower'
  published: no      
,
  id: 5
  title: 'Calvin, why do you keep calling me Calvin?'
  published: yes      
]

App.Router.map ->
  @resource 'posts',
    path: '/'
    
App.PostsRoute = Ember.Route.extend
  model: ->
    @store.find 'post'

App.PostsController = Ember.ArrayController.extend
  actions:
    sort: (property) ->
      if @get('isSorted') and (@get('sortProperties')[0] is property)
          @toggleProperty('sortAscending')
      else
        @set('sortProperties', [property])
        @set('sortAscending', yes)

      # Returning false prevents the action from bubbling
      return false

App.SortingKeyComponent = Ember.Component.extend
  tagName: 'dd'
  classNameBindings: ['isSorted:active', 'isAsc:asc', 'isDesc:desc']
  isSorted: (->
    @get('sortProperties')?[0] is @get('key')
  ).property('sortProperties')
  isAsc: (->
    @get('isSorted') and @get('sortAscending')
  ).property('isSorted', 'sortAscending')
  isDesc: (->
    @get('isSorted') and not @get('sortAscending')
  ).property('isSorted', 'sortAscending')
  click: ->
    @sendAction 'action', @get('key')