Ember Time

This article is now seriously out of date. Please refer to Ember’s official guides.

What follows is a re-publication of a README.

The README in question turned out a little better-written and more instructive than I had expected so I figured it deserved to be published as a proper little piece.

If you think this is shameless recycling of material, feel free to troll me on Twitter.


A friend recently asked about the best approach for implementing a particular feature in Ember. They wanted to use moment.js to show createdAt times in ‘time ago’ format – and wanted them to update each minute.

This is a common feature, but it’s not covered in Ember’s guides and appears to fall slightly outside of the golden path. So let’s try implementing it, and hopefully we’ll get a chance to use some of the lesser-known tools in Ember’s API.

First up, let’s figure out how we want to use this in our templates. Let’s say we want a fromNow helper to match Moment’s API. Let’s add that to our application template:

<script type="text/x-handlebars">
  <p>Created {{fromNow valueBinding="createdAt"}}</p>
</script>

If we reload the page we get an error telling us that fromNow is not defined anywhere. So let’s define it.

This feature involves continued behaviour, so we probably don’t want a one-shot helper. Instead, let’s have our helper delegate to a taylored view class.

Ember.Handlebars.helper('fromNow', App.FromNowView);

Now to define that view class. We probably want it to use the time tag and render the result of running Moment’s fromNow method on whatever its value is.

App.FromNowView = Ember.View.extend({
  tagName: 'time',

  template: Ember.Handlebars.compile('{{view.output}}'),

  output: function() {
    return moment(this.get('value')).fromNow();
  }.property('value')
});

Note that we use view.output rather than simply output. This is because Ember’s views try their best to get out of the way of the surrounding context. If we want to access a property of the view in it’s template, we need to be specific. Also note that the view class will need to be defined above before the helper, as it references it directly.

If we refresh the page now, we’ll actually see the reasonable output of ‘Created a few seconds ago’. This is because our view’s value is not defined and moment(undefined) creates a moment object for the current time.

Let’s define createdAt so it’ll become the value of our view.

We’re rendering the view in the application template, which is backed by the singleton instance of our ApplicationController, so let’s set createdAt there:

App.ApplicationController = Ember.Controller.extend({
  createdAt: new Date(2011, 3, 30)
});

Refreshing the page should show something like ‘Created 2 years ago’. This is great, but not so good for our demo, so let’s say that createdAt is set to the current time when the app is booted.

App.ApplicationController = Ember.Controller.extend({
  createdAt: new Date()
});

We’re back to our ‘Created a few seconds ago’ output, but we know everything’s bound together properly now. It’s time to make this clock tick.

Our FromNowView probably needs some sort of tick method to trigger the re-render. Digging into Ember’s API docs reveals some very helpful methods in the Ember.run namespace. We’ll also need to start this clock ticking, so let’s use the didInsertElement hook.

App.FromNowView = Ember.View.extend({
  // ...

  didInsertElement: function() {
    this.tick();
  },

  tick: function() {
    Ember.run.later(this, function() {
      console.log('tick');
      // Re-render the view somehow
      this.tick();
    }, 1000);
  }
});

If we open the javascript console now, we should see ‘tick’ written to the log every second. That’s a start, now we need to figure out how to re-render the view. Digging again into Ember’s API docs, we find a method called notifyPropertyChange on Ember.View. That sounds like it might work. Let’s give it a go.

App.FromNowView = Ember.View.extend({
  // ...

  tick: function() {
    Ember.run.later(this, function() {
      console.log('tick');
      this.notifyPropertyChange('value');
      this.tick();
    }, 1000);
  }
});

Leave the page for 60 seconds and we should see ‘Created a few seconds ago’ automatically update to ‘Created a minute ago’ and so on.

This is a good start, but there’s a little problem — there’s nothing to clean up our tick method. If we switch states away from this template tick might get called when the view is no longer on display and we’ll get errors, not to mention memory leaks.

Digging into Ember’s docs again, we find views have a willDestroyElement hook and Ember provides Ember.run.cancel to cancel deferred execution. So we’ll need to keep a handle on our deferred tick execution and be sure to cancel it when the view is destroyed.

App.FromNowView = Ember.View.extend({
  // ...

  tick: function() {
    var nextTick = Ember.run.later(this, function() {
      console.log('tick');
      this.notifyPropertyChange('value');
      this.tick();
    }, 1000);
    this.set('nextTick', nextTick);
  },

  willDestroyElement: function() {
    var nextTick = this.get('nextTick');
    Ember.run.cancel(nextTick);
  }
});

To check this has all worked, let’s rearrange the app a bit. We’ll create a clock route that contains our fromNow helper, and jump back to the index route to check tick is not still getting invoked. We’ll also need to move the value of createdAt to a new ClockController.

App.Router.map(function() {
  this.route('clock');
});

// ...

App.ClockController = Ember.Controller.extend({
  createdAt: new Date()
});
<script type="text/x-handlebars">
  <h1>Ember Time</h1>

  <nav>
    {{#linkTo index}}Home{{/linkTo}}
    {{#linkTo clock}}Clock{{/linkTo}}
  </nav>

  {{outlet}}
</script>

<script type="text/x-handlebars" data-template-name="clock">
  <p>Created {{fromNow valueBinding="createdAt"}}</p>
</script>

If everything’s worked, now when we navigate to ‘Clock’ we should see ‘Created a few seconds ago’ and if we leave the app in this state long enough we’ll see ‘Created a minute ago’. We should also see the console logging ‘tick’ every second and—all being well—when we navigate back to ‘Home’ we’ll see the console stops logging ‘tick’.

You can find all of this put together in the Ember Time Repo and very basic demo.

I hope this little tale of Ember development proves useful to someone out there. If you’ve read through all this and still have a few minutes to spare, I recommend this inspirational video.