Mixins in Ember.js

by Kasper Tidemann

When you write code, you often run into situations where you need similar behavior in different parts of your project. And if you know the DRY principle, you know it’s bad to repeat yourself in code. Ember.js offers a couple of ways to tackle this.

The idea of using extend() to extend classes (or what is essentially an approximated equivalent in JavaScript) works great a lot of times. For instance, Ember.Textfield extends Ember.View, so an instance of Ember.Textfield will have the classNames property available to it since it’s inherited from Ember.View.

For a project of mine, I have created an Ember.InputView that works as the base class for creating an input field (which is a <div> with contenteditable enabled). Whenever I need to mimick a textarea, I make use of an Ember.InputareaView which extends the Ember.InputView. This allows me to put logic in the base class for handling validation, pasting of text etc.

However, you may have logic that is not limited to just views. Imagine you have an App.Stock and an App.Bond model:

App.Stock = DS.Model.extend({
  symbol: DS.attr('string'),
  value: DS.attr('number')
});

App.Bond = DS.Model.extend({
  name: DS.attr('string'),
  value: DS.attr('number'),
  duration: DS.attr('number')
});

For stocks and bonds, it’s imperative to know when pricing information was last updated, since the prices of such financials can change rapidly over short periods of time.

Keeping track of when a record was originally fetched from the server doesn’t belong on a record per se because of the metadata nature of such details. So, in order to support this both in App.Stock and App.Bond, we could use a mixin:

App.LastUpdatedMixin = Ember.Mixin.create({
  lastUpdated: null,
  ticker: null,
  timeSinceLastUpdated: (function() {
    return (this.get('ticker') - this.get('lastUpdated')) / 1000;
  }).property('lastUpdated', 'ticker'),
  didLoad: function() {
    this.set('lastUpdated', Date.now());
    var self = this;
    setInterval({ self.set('ticker', Date.now()) }, 1000);
  }
});

All we need is to extend our two models using the mixin. This is done by supplying our mixin as the first argument to extend():

App.Stock = DS.Model.extend(App.LastUpdatedMixin, {
  ...
});

App.Bond = DS.Model.extend(App.LastUpdatedMixin, {
  ...
});

Now, if we have an instance of App.Stock, we can call stock.get('timeSinceLastUpdated') in order to get the decimal time in seconds since the stock data was updated (or, in Ember Data lingo: the decimal time in seconds since the record was loaded).

Ember.js makes heavy use of mixins internally. For instance, did you know that an Ember.ArrayController is actually an Ember.ArrayProxy mixed with Ember.ControllerMixin and Ember.SortableMixin? Here is a snippet from the source:

Ember.ArrayController = Ember.ArrayProxy.extend(Ember.ControllerMixin, Ember.SortableMixin, {
  ...
});

The use of Ember.SortableMixin is what adds the sortProperties and sortAscending properties to an ArrayController, for example.

Using mixins is a great way of keeping your code DRY and easy to maintain. I highly recommend reviewing your code often in order to identify possible uses of mixins, especially when dealing with larger applications. Using mixins tend to lower the complexity of the code base in the long run.