I'm all about Ember.js recently

Ember.run.bind

Ember has several great features that are not widely known, like its Dependency Injection system I wrote about last week.

It has also really handy API methods in hiding. One usually stumbles on them while looking for something in the superbly commented source code.

I hereby introduce such a handy method and hope to make that into a recurring thing. Who knows? Eventually this might also grow into a series.

If it does, I would have to find a catchy name for it. “Ember Chispas” came to mind and rings nice but unfortunately Jeffrey Biles already has a monopoly on it via his “Ember Sparks” series. (Chispa is Spanish for spark.) We’ll have to talk, Jeffrey :)

Anyway, let’s see the first thingie.

… and in the runloop bind them

The run loop in Ember deserves its own blog post series, and I am probably not the one who will write it. (The official guide on the subject is a good place to start, Alex Matchneer’s “Everything You Never Wanted to Know About the Ember Run Loop” is more elaborate).

For the sake of this chispa, it suffices to state that any async callback from a 3rd party javascript library needs to be wrapped into an Ember.run.

Blatantly stealing the example from the source code, here is how this is done:

1
2
3
4
5
6
var that = this;
jQuery(window).on('resize', function(){
  Ember.run(function(){
    that.handleResize();
  });
});

See that beautiful var that=this;? It can be got ridden of via Ember.run.bind:

1
jQuery(window).on('resize', Ember.run.bind(this, this.handleResize));

Less code to write and all the mustaches are gone (not that there is anything wrong with {{mustaches}}, of course).

Dependency Injection in Ember.js - First Steps

Dependency injection (DI) is a less-known, yet powerful feature of the framework we know and love called Ember.js. It works through the container and is comprised of two parts: registration and injection.

The framework itself uses it so that the objects instantiated through the container have the dependencies correctly initialized on them (for the theoretical programmers out there, the Ember DI system is thus probably considered a construction-time DI). The API methods are exposed to application developers for them to register and inject their own objects.

In this first blog post, I’ll introduce the main concepts. In a couple of further blog posts I intend to dig deeper, give examples of what it can be used for and show a few gotchas.

Step 1 - Registration

To be able to inject an entity onto others it first has to be registered on the container. Think of the container as the birthplace of objects in Ember. It knows how to instantiate objects and look up factories by their name.

Throughout the post, I’ll use the example of a store. If you have worked with ember-data, the store is the principal piece. It creates model objects and finds them by their type, id or other criteria so you probably want it to be accessible from routes and controllers alike.

If you have not come through such a store, don’t despair. It is but a handy example to illustrate dependency injection and how it works for a singleton object.

Let’s assume we have a reference to the store “class” in a variable called Store. To register said store, we write the following:

1
2
3
4
5
6
7
Ember.Application.initializer({
  name: "store",

  initialize: function(container, application) {
    container.register('store:main', Store, { singleton: true });
  }
});

The container now knows the store by the name store:main. That is the name it has to be referred by subsequently. The singleton: true option instructs the container not to instantiate a new object each time but use a singleton instead.

Step 2 - Injection

Once the object/factory is registered, it can be used on other objects in the application. As mentioned above, a store is best used from controllers and routes. So let’s inject them there:

1
2
3
4
5
6
7
8
9
Ember.Application.initializer({
  name: "injectStore",
  before: "store",

  initialize: function(container, application) {
    application.inject('controller', 'store', 'store:main');
    application.inject('route', 'store', 'store:main');
  }
});

The main method, application.inject takes as its first argument the object(s) where we want the injection to happen, then the injected name and then the name of what should be injected. The “what should be injected” has to be the same name we had registered the object previously with.

The first parameter, controller and route define what is called a type injection. This means the injected name will be available on all instances of the type, all controllers and routes in this case. The injected name is store and it refers to a single instance of the store class since that’s how we defined it at registration time.

We could also just define a simple injection. In a somewhat hypotethical example, let’s assume we only need the store on the artists route. We write the following to achieve that:

1
2
3
4
5
6
7
8
Ember.Application.initializer({
  name: "injectStore",
  before: "store",

  initialize: function(container, application) {
    application.inject('route:artists', 'store', 'store:main');
  }
});

Step 3 - Putting the two pieces together

Let’s say we want to create an artist object in a route and that creating objects happens through the store (as it does in ember-data, e.g). The following snippets set up the injection:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Ember.Application.initializer({
  name: "store",

  initialize: function(container, application) {
    container.register('store:main', App.Store, { singleton: true });
  }
});

Ember.Application.initializer({
  name: "injectStore",
  before: "store",

  initialize: function(container, application) {
    application.inject('route', 'store', 'store:main');
  }
});

This makes the singleton instance of the store available on all routes of the application so we could, for example, write this:

1
2
3
4
5
App.IndexRoute = Ember.Route.extend({
  model: function() {
    return this.store.createRecord('artist');
  }
});

I also created a jsbin to show this in action, although admittedly there is little action at this point.

Why DI?

This is all fine and dandy, but what problems does dependency injection solve? First and foremost, it decouples the pieces of your application. Dependencies are not hardcoded but injected at construction time. This makes your components easier to use together since pieces can be swapped in and out as needed.

It is also the de facto way of adding the objects that are not provided by Ember.js to your application. The store is an example of such an object but you can also add analytics integration, a pool of workers and other fancy things easily.

An added benefit is improved unit testing since you can inject mocks as cooperators and verify calls made on those.

We are going to look at other aspects of DI in Ember.js in more detail in a next post. Stay tuned and prepare your mojitos.

Observable Path Patterns in Ember

Property paths are the heart and soul of Ember.js apps. You use them in templates and you define dependencies for computed properties and observers through property paths.

In this post, I concentrate on this latter and show you various ways of setting up these dependencies through a practical example. There is a pretty good section in the guides about one path pattern. Here, I intend to cover more (all?) of them.

Badges are back

I am going to build on the badges “micro-app” that I had started to develop in my previous post about getters in setters.

There are two minimal model classes, User and Badge:

1
2
3
4
5
6
7
8
9
App.User  = Ember.Object.extend({
  name: ''
});

App.Badge = Ember.Object.extend({
  name: '',
  score: 0,
  unlocked: false
});

We also create a couple of instances to have some data to show and work with:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var cory = App.User.create({ name: "Cory Filibuster" });

var rook = App.Badge.create({
  name: "R00k",
  score: 1,
  unlocked: true
});

var taciturn = App.Badge.create({
  name: "Taciturn",
  score: 10
});

var talkative = App.Badge.create({
  name: "Talkative",
  score: 100
});

var hemingway = App.Badge.create({
  name: "Hemingway",
  score: 1000
});

Our application looks like this initally:

Initital state

Simple property chain

The simplest, non-aggregate property path is just a series of names, connected by dots. This designates a property that you can arrive at by walking the path, segment by segment, where each of them gets you to another object until you finally reach the property.

(If a path is very long, you should probably think about the dependencies between your objects and the structure of your code.)

You see that the profile panel has the user’s first name as its header. The property that gets displayed there can be defined by such a path:

1
2
3
4
5
6
App.IndexController = Ember.ArrayController.extend({
  (...)
  firstName: function() {
    return this.get('user.name').split(/\s+/)[0];
  }.property('user.name'),
});

This sets up a computed property (CP) that will be recomputed whenever user.name changes. The arguments to the property call are called the dependent keys of the CP and you can pass as many as you would like (although, thanks to the various property path patterns, you will rarely need a lot).

Now, whenever the name property of the user property on the controller changes, firstName is recomputed and this change gets propagated to all the instances where firstName is used (e.g in the header of the panel).

Above that, the user.name key also triggers a change if the user object itself changes. To see that, we turn to the thing you should only ever use for demo purposes, the __container__ object:

1
2
var maggie = App.User.create({ name: "Maggie Raindance" });
App.__container__.lookup('controller:index').set('user', maggie);

You can see the name change in the header right away:

User name changes

Aggregate property paths

On other occasions, a CP should depend on an array of items. Whenever something gets added to or removed from the array, the property needs to be updated.

One example of that is the number of badges in the profile panel:

1
2
3
4
5
6
App.IndexController = Ember.ArrayController.extend({
  (...)
  badgeCount: function() {
    return this.get('model').length;
  }.property('model.[]'),
});

The model here is the array of badges so when we add another one through the New badge panel, badgeCount gets its new value:

Badge count gets updated

What I said about the user.name path triggering an update when the user changes also holds true here. If the array of badges was swapped out for another array, it would trigger the recalculation of badgeCount.

Aggregate property path with a specified property

There are cases where the value of the CP becomes stale also when the items in the dependent array stay the same, but a certain property of one of them changes. Ember has a way to express this very succintly.

The example is the “Total score” in the profile panel:

1
2
3
4
5
6
7
App.IndexController = Ember.ArrayController.extend({
  (...)
  totalScore: function() {
    var sum = function(s1, s2) { return s1 + s2; };
    return this.get('model').getEach('score').reduce(sum);
  }.property('model.@each.score'),
});

This is the most inclusive of the patterns we have seen so far. It prompts an update if the model changes, if any item is added or removed and also if the score of any item changes. If we type this at the console:

1
App.__container__.lookup('controller:index').set('model.lastObject.score', 200);

, then the total score changes accordingly, even though no item was inserted or deleted:

Total score

Brace yourself

To present the next pattern, let’s assume that not all badge scores need to be tallied to get the total but only the unlocked ones (which makes total sense). So the dependent keys for totalScore needs to account for that. That’s pretty easy:

1
2
3
4
5
6
7
App.IndexController = Ember.ArrayController.extend({
  (...)
  totalScore: function() {
    var sum = function(s1, s2) { return s1 + s2; };
    return this.get('model').filterBy('unlocked').getEach('score').reduce(sum);
  }.property('model.@each.score', 'model.@each.unlocked'),
});

When the second badge is unlocked, the score jumps from 1 to 11 (and the number of badges from 1 to 2), so the dependent keys work fine:

1
App.__container__.lookup('controller:index').get('model').objectAt(1).set('unlocked', true);

Unlocked property change triggers update

Starting with Ember 1.4.0, though, there is a more concise way to define the same, called “property brace expansion”. It works very similar to argument expansion in the shell:

1
2
3
4
5
6
7
App.IndexController = Ember.ArrayController.extend({
  (...)
  totalScore: function() {
    var sum = function(s1, s2) { return s1 + s2; };
    return this.get('model').filterBy('unlocked').getEach('score').reduce(sum);
  }.property('model.@each.{score,unlocked}'),
});

This establishes that totalScore should be recomputed if either the score or unlocked properties of any item in the model array changes.

An important restriction of property brace expansion is that the expansion part can only be placed at the end of the path, so e.g property('{foo,bar}.baz') will not have the desired effect.

Computed property macros are the bee’s knees

Computed property macros have several advantages. They are very expressive, very performant and perhaps most importantly more robust than typing out the property path patterns by hand where a typo can cause a considerable amount of head-scratching.

They are also a joy to work with and compose. In fact, all the CP definitions above can be re-defined by using only macros:

1
2
3
4
5
6
7
App.IndexController = Ember.ArrayController.extend({
  (...)
  badgeCount: Ember.computed.alias('unlockedBadges.length'),
  unlockedBadges: Ember.computed.filterBy('model', 'unlocked'),
  unlockedScores: Ember.computed.mapBy('unlockedBadges', 'score'),
  totalScore: Ember.computed.sum('unlockedScores'),
});

They have one big disadvantage, though. It is very hard to use them in a blog post to explain property path patterns.

(The code presented here can be found as a gist on Github)

ps. Yes, that Maggie Raindance.)

Ember.js Getters and Setters

To make all the magic fairy-dust sprinkling efficient, like auto-updating templates and computed properties, Ember uses getters and setters instead of accessing properties directly the javascript (and dare I say, the Angular) way. At its most simplest form, it is object.get(property) and object.set(property).

Howevers, it would not be Ember if we were not provided methods on top of these primitives to make our hard lives as web developers simpler. In the following post, I am going to show (some of) these methods through an example, so let’s go.

Badges, badges, I want more badges

I am certainly in favor of having badges in every application. Discourse is actively discoursing their badge system so I quickly sketched out something to get ahead of them.

Let me just paste the code here and then use it to explain what the getter and setter methods are good for.

You can quickly scroll through the below templates. They are mostly there so that you get the whole picture. The stuff relevant to this discussion is found in the javascript code.

<script type="text/x-handlebars">
  <div class="container">
    <div class="panel panel-primary">
      <div class="panel-heading">
        <h3 class="panel-title">Your badges, sir/ma'am.</h3>
      </div>
      <div class="panel-body">
        {{outlet}}
      </div>
    </div>
  </div>
 </script>
<script type="text/x-handlebars" data-template-name="index">
  <ul class="list-group">
    {{#each badge in arrangedContent}}
      <li {{bind-attr class=":list-group-item badge.unlocked::locked"}}>
        {{badge.name}}
        <span class="badge">{{badge.score}}</span>
      </li>
    {{/each}}
    <li class="list-group-item">
      <em>Total:</em>
      <span class="pull-right">{{totalScore}}</span>
    </li>
  </ul>
  <div id="new-badge" class="row">
    <span class="col-xs-6">
      {{input class="input" type="text" placeholder="Badge name" value=name}}
    </span>
    <span class="col-xs-4">
      {{input class="small-input" type="text" placeholder="Score" value=score action="addBadge"}}
    </span>
    <span class="col-xs-2">
      <button class="btn btn-primary btn-sm pull-right" type="button"
          {{action "addBadge"}}>
          Add
      </button>
    </span>
  </div>
  <div id="unlock-all" class="row">
    <div class="col-xs-12">
      <button class="btn btn-danger btn-md" type="button"
        {{action "unlockAll"}}>
        Unlock all
      </button>
    </div>
  </div>
</script>

First, the badge class is defined and some badges created so that we have something to work with:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
App = Ember.Application.create();

App.Badge = Ember.Object.extend({
  name: '',
  score: 0,
  unlocked: false
});

var rook = App.Badge.create({
  name: "R00k",
  score: 1,
  unlocked: true
});

var talkative = App.Badge.create({
  name: "Talkative",
  score: 10
});

var hemingway = App.Badge.create({
  name: "Hemingway",
  score: 1000
});

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

App.IndexRoute = Ember.Route.extend({
  model: function() {
    return [rook, talkative, hemingway];
  }
});

getProperties and setProperties

The first couple of methods come handy when working with a single object but multiple properties.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
App.IndexController = Ember.ArrayController.extend({
  sortProperties: ['score'],
  sortAscending: true,

  (...)

  actions: {
    addBadge: function() {
      var newBadgeProperties = this.getProperties(['name', 'score']);
      newBadgeProperties.score = parseInt(newBadgeProperties.score, 10);

      var newBadge = App.Badge.create(newBadgeProperties);
      this.get('model').pushObject(newBadge);
      this.setProperties({ name: '', score: '' });
    },

    (...)
  }
});

On line 9, we want to create a new object with the values provided in the input boxes (addBadge is the action that gets triggered when the Add button is clicked, check the template). getProperties will create a javascript object creating key-value pairs for the passed properties. So the above might e.g yield { name: "Silent Bob", score: "2" }. That gets directly passed in to create a new badge object.

On line 14, we use the mutating pair of getProperties to reset the input fields. Pretty straightforward.

getEach and setEach

Ember has us covered when we are working with an array and want to get (or set) the same property of each item.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
App.IndexController = Ember.ArrayController.extend({
  sortProperties: ['score'],
  sortAscending: true,

  totalScore: function() {
    var sum = function(s1, s2) { return s1 + s2; };
    return this.get('model').getEach('score').reduce(sum);
  }.property('model.@each.score'),

  actions: {
    (...)

    unlockAll: function() {
      this.get('model').setEach('unlocked', true);
    }
  }
});

When the “Unlock all” button launches the unlockAll action, it calls setEach on the badges (line 14), making all of them unlocked (you can verify this in the demo by seeing the color of the badge names turn darker - their css class has been updated). Another advange of setEach is that it guards against calling set on null or undefined values.

You might know the reader counterpart, getEach as mapBy. It goes through the array and makes another array by getting the passed property on each item. So in the above example (line 7), we first collect the score for each badge and then sum them up by way of reduction. (A shiny example of a non-distributed map-reduce :) ).

A macro can make nice code nicer

I have used a reduce computed macro before to set up sorting. I have gotten the hang of it and realized I could use another couple of them to make the above code simpler (and more performant):

1
2
3
4
5
6
7
8
9
App.IndexController = Ember.ArrayController.extend({
  sortProperties: ['score'],
  sortAscending: true,

  scores: Ember.computed.mapBy('model', 'score'),
  totalScore: Ember.computed.sum('scores'),

  (...)
});

The problem is that the subject under discussion, getEach is gone now, so pretend you did not see this.

Finally, here is the jsbin, should you decide to play around with it:

Getter and Setters

I hope some of that sticks and you’ll write less “bare” gets and sets.

Sorting Arrays in Ember.js by Various Criteria

I concluded the previous post on route refactoring by saying that the fact that the artists.songs route now has the songs as its model will pay dividends in the future.

I will show one thing that becomes really easy this way and also one less-known Ember feature that builds on top of that.

Showing list items in a certain order

Even with our superb routing, songs for an artist appear in random order, differently at each load of the application. This is confusing and it is now easy to remedy.

The controller backing the artist.songs template is now an ArrayController so establishing a sort order is a piece of cake:

1
2
3
4
5
6
App.ArtistSongsController = Ember.ArrayController.extend({
  (...)
  sortAscending: false,
  sortProperties: ['rating', 'title'],
  (...)
});

What I say with these sorting-related definitions is that I want songs to be shown in descending order of first their ratings and then -should the rating be the same- by title. That’s all there is to it, those tunes are now aptly sorted:

Ascending ordering

Sorting by multiple properties, in different directions

There is a slight deficiency in the above sorting API. It cannot sort multiple properties if the sorting is to be done ascending for some properties and descending for others. The sortAscending flag is “global” for the whole sortProperties array.

Taking the above example, it is a perfectly valid request to have songs first ordered in descending order of their rating and then in ascending order of their title. But how can we do that?

Reduce computed macros

The Ember.computed.sort macro in the reduce_computed_macros package provides a clean way to do exactly that:

1
2
3
4
5
6
App.ArtistSongsController = Ember.ArrayController.extend({
  (...)
  sortProperties: ['rating:desc', 'title:asc'],
  sortedSongs: Ember.computed.sort('model', 'sortProperties'),
  (...)
});

The API is so explicit it does not need any explanation. In order for the songs to appear in the specified order, we now need to iterate through sortedSongs so the #each in the template becomes #each sortedSongs.

Let’s take a look at how it has changed the order of the songs:

Perfect ordering

Perfect, I would say that is the ordering that makes most sense.

Wait, it gets better still

When first playing with Ember.computed.sort, my initial try was to set up sortedSongs like that:

1
  sortedSongs: Ember.computed.sort('model', ['rating:desc', 'title:asc']),

That, however, throws an error. Taking a look at the code and the tests, I realized that the second argument needs to be a property on the same object the sorting is defined on. This seems “over-engineered” at first but in fact opens up further possibilities.

Under the hood, Ember.computed.sort sets up an observer not only to the array being sorted but also to the sort properties. That way, when the sorting criteria changes, the list is reordered and rerendered automatically. This makes it possible for the criteria to be easily modified via user action.

To give you an example, I’ll make a button set the rating to happen only by title, in ascending alphabetical order:

<script type="text/x-handlebars" data-template-name="artist/songs">
  {{#if canCreateSong}}
    <div class="list-group-item">
      (...)
      <button class="btn btn-link pull-right" {{action "sortBy" "title:asc"}}>Sort by title</button>
    </div>
  {{/if}}
  (...)
</script>

The sortBy action handler sets the passed string as the value for the sortProperties which will rearrange the list:

1
2
3
4
5
6
7
8
9
10
11
12
App.ArtistSongsController = Ember.ArrayController.extend({
  (...)
  sortProperties: ['rating:desc', 'title:asc'],
  sortedSongs: Ember.computed.sort('model', 'sortProperties'),

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

Indeed when the link is clicked, the songs become ordered by title only:

Ordering by only title

Declarative sorting

Let me finish by pointing out that this arrangment makes it possible to have several actions that set (or modify) the ordering criteria, without having to write a single line of code to implement the sorting itself.

You gotta love this.

A Common Resource Route Pattern in Ember.js

Intro

A very common pattern in web applications, be them server- or client side, is resource URLs. We might have a list of users that we want to show at /users and then different pages related to the user which is encoded in the URL. These might be e.g /users/dave-hopkins/activity and /users/dave-hopkins/followers.

The pattern is a top-level URL to list all the resource instances, and then separate pages to display pieces of information regarding specific resource instances.

Artists and songs

That’s exactly what I did for the the Rock & Roll application, where the routes were defined as such:

1
2
3
4
5
App.Router.map(function() {
  this.resource('artists', function() {
    this.route('songs', { path: ':slug' });
  });
});

The simplest thing that works. However, the above is not ideal especially when more pages (or views, if you will) are added below the artists resource route. That’s because the singular artist instance is encoded in the songs route, by having its identifier (in this case, slug) in the path of that route.

Imagine we need to add additional info about each band. Just blindly extending the above URL scheme, this would become:

1
2
3
4
5
6
App.Router.map(function() {
  this.resource('artists', function() {
    this.route('songs', { path: ':slug/songs' });
    this.route('info',  { path: ':slug/info' });
  });
});

The cracks start to show. The artist for both the artists.songs and the artists.info routes would have to be fetched in both routes, with identical code. Nested routes -and how it lends itself to a nested UI- is truly a masterpiece, a shining emerarld on Ember’s crown. It would be a pity not to take advantage of it.

DRY up those routes

So we established that the problem is having the artist “encoded” in all routes below the top-level artists resource. The solution is consequently pretty straightforward -this always seems to be the case in retrospective-, let’s extract the path segment that represents the artist:

1
2
3
4
5
6
7
App.Router.map(function() {
  this.resource('artists', function() {
    this.resource('artist', { path: ':slug' }, function() {
      this.route('songs');
    });
  });
});

With the introduction of the artist resource, the duplication is gone, but we are not done yet. First, we have to define the route and set up its model hook. Second, since the “routing table” has changed, we’ll have to adjust route names and code that uses them. Since the naming conventions in Ember have the route names as their basis, we’ll probably have to change code in several places.

Route changes

Resource routes reset the routing namespace, so the route that corresponds to the artist route name in the table is App.ArtistRoute:

1
2
3
4
5
6
7
8
9
10
11
App.ArtistRoute = Ember.Route.extend({
  model: function(params) {
    return Ember.RSVP.Promise(function(resolve, reject) {
      App.Adapter.ajax('/artists/' + params.slug).then(function(data) {
        resolve(App.Artist.createRecord(data));
      }, function(error) {
        reject(error);
      });
    });
  }
});

That is exactly what we had for App.ArtistsSongsRoute in the previous version, which makes sense. The artist is now fetched one route level higher.

For simple, non-resource routes, the name of the route is the name of the resource route above (if it exists) plus the name of the route itself. In this case, the route name is artist.songs which gets resolved as App.ArtistSongsRoute):

1
2
3
4
5
6
7
8
9
10
11
App.ArtistSongsRoute = Ember.Route.extend({
  model: function(params) {
    return this.modelFor('artist').get('songs');
  },

  setupController: function(controller, model) {
    this._super(controller, model);
    controller.set('artist', this.modelFor('artist'));
  },
  (...)
});

The first interesting thing is modelFor. It gets the model for another, already resolved route. In Ember route models are resolved stepping down from the top-level application route. That means that at this point we can be certain that the artist route already has its model, the artist instance resolved.

The model of this route is simply the songs belonging to that artist.

The other interesting bit is setupController. We have already come across this hook before; it is the place to do additional setup -above fetching the model and deciding which template to render- for the controller. Since we’ll want to display artist-related data in the template, we store it in an artist property and we make sure to call _super, the implementation of this hook in Ember.Route, that sets the controller’s model property to the model argument in this method.

Templates & controllers

The mechanical part of the routing update is to replace all occurrences of the artists.songs route name to artist.songs.

What deserves more attention is that the controller for artist.songs now has the songs of the artist as its model, not the artist itself. That means that we should adjust the controller type it extends:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
App.ArtistSongsController = Ember.ArrayController.extend({
  artist: null,

  newSongPlaceholder: function() {
    return 'New ' + this.get('artist.name') + ' song';
  }.property('artist.name'),

  songCreationStarted: false,
  canCreateSong: function() {
    return this.get('songCreationStarted') || this.get('length');
  }.property('songCreationStarted', 'length'),

  (...)
});

All changes are made necessary by the model change. Properties of the artist now need to be prefixed by artist (e.g name => artist.name) while properties of the songs no longer need to have the songs prefix since it is the model (e.g songs.length => length).

This also holds true of the template. To give an example, rendering the stars for each song can becomes more concise:

<script type="text/x-handlebars" data-template-name="artist/songs">
  (...)
  {{#each}}
    <div class="list-group-item">
      {{title}}
      {{star-rating item=this rating=rating maxRating=5 setAction="setRating"}}
    </div>
  {{else}}
  (...)
</script>

The #each helper, without parameters, loops through the items in the model of the template, in our case, the songs, which is exactly what we want.

That wraps up our route sanitizaion. In the next post, we will take advantage of the benefit that the songs route now has the artist’s songs as its model.

Readers’ Letters: Making an Ember.js Component Even Better

This is Part 3 of a mini-series on components. Here are the preceding posts:

Part 1: Convert a view into a component

Part 2: Making an Ember.js Component More Reusable


Last time I showed a way to make the star-rating component more reusable. The solution employed a useful, low-level method, Ember.addObserver and its destructive sibling, Ember.removeObserver.

A couple of my readers offered alternative solutions that, I think, make the code simpler without harming reusability of the component.

This post is going to be sort of a “Readers’ Letters”, showing these solutions and explaining how they are better than my original take.

Ember.defineProperty

David Chen chimed in in the comments suggesting using Ember.defineProperty instead of setting up and tearing down an observer:

1
2
3
4
5
6
7
8
9
10
11
App.StarRatingComponent = Ember.Component.extend({
  classNames: ['rating-panel'],

  numStars:  Ember.computed.alias('maxRating'),

  defineFullStars: function() {
    Ember.defineProperty(this, 'fullStars', Ember.computed.alias('item.' + this.get('ratingProperty')));
  }.on('init'),
  (...)

});

Ember.defineProperty makes a fullStars property on the component which is an alias of item.rating (or item.score). We can concatanate ‘item.’ with that property name in the body of defineFullStars, something I could not get around earlier.

Finally, the on function, an extension to the Function prototype sets up a listener and gets called when the component’s init method is executed.

It is better, because there is a lot less code, it is more comprehensible and there is no need for a second step, tearing down the observer.

Passing in the value rating directly

Ricardo Mendes takes my approach one step further and shows that it is unnecessary to pass in the name of the ratingProperty.

Passing in the value of the property directly takes separation of concerns to the next level. The component does not need to know about the name of the rating property, all it needs to know is its value:

<script type="text/x-handlebars" data-template-name="artists/songs">
  (...)
  {{#each songs}}
    <div class="list-group-item">
      {{title}}
      {{star-rating item=this rating=rating maxRating=5 setAction="setRating"}}
    </div>
  (...)
  {{#each}}
</script>

What changed is that instead of ratingProperty=”rating” (which could be ratingProperty=”score”), the value of the rating itself is passed in. Note that there are no quotes around rating which establishes a binding.

The definition of the fullStars property now could not be simpler and more expressive:

1
2
3
4
5
6
7
App.StarRatingComponent = Ember.Component.extend({
  classNames: ['rating-panel'],

  numStars:  Ember.computed.alias('maxRating'),
  fullStars: Ember.computed.alias('rating'),
  (...)
});

Since the component does not know about the rating property, it can’t set the item’s rating which is a good thing since it’s not its responsiblity. It just sends an action to its context with the appropriate parameters:

1
2
3
4
5
6
7
8
9
10
11
12
App.StarRatingComponent = Ember.Component.extend({
  (...)
  actions: {
    setRating: function() {
      var newRating = parseInt($(event.target).attr('data-rating'), 10);
      this.sendAction('setAction', {
        item: this.get('item'),
        rating: newRating
      });
    }
  }
});

This action is then handled by the controller:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
App.ArtistsSongsRoute = Ember.Route.extend({
  (...)
  actions: {
    setRating: function(params) {
      var song = params.item,
          rating = params.rating;

      song.set('rating', rating);
      App.Adapter.ajax('/songs/' + song.get('id'), {
        type: 'PUT',
        context: this,
        data: { rating: rating }
      })
      (...)
    }
  }
});

Clear separation of concerns, less and more expressive code.

Replacing data-rating

A further simplification comes from Tom de Smet.

He rightfully pointed out that there is no need to get the rating that was clicked on via a data attribute. It is already known at template rendering time and can thus be passed to the action helper.

So this:

<script type="text/x-handlebars" data-template-name="components/star-rating">
  {{#each stars}}
    <span {{bind-attr data-rating=rating}}
      {{bind-attr class=":star-rating :glyphicon full:glyphicon-star:glyphicon-star-empty"}}
      {{action "setRating"}}>
    </span>
  {{/each}}
</script>

becomes this:

<script type="text/x-handlebars" data-template-name="components/star-rating">
  {{#each stars}}
    <span
      {{bind-attr class=":star-rating :glyphicon full:glyphicon-star:glyphicon-star-empty"}}
      {{action "setRating" rating}}>
    </span>
  {{/each}}
</script>

And then setRating simply receives the new rating as an argument:

1
2
3
4
5
6
7
8
9
10
11
App.StarRatingComponent = Ember.Component.extend({
  (...)
  actions: {
    setRating: function(newRating) {
      this.sendAction('setAction', {
        item: this.get('item'),
        rating: newRating
      });
    }
  }
});

Instead of adding an extra data-binding property, we rely on the action helper and we do not need the additional fetching (and parsing) of the property.

Give credit where credit is due

This week’s post was made possible by David, Ricardo and Tom. Their insights made the star-rating component impeccable for which they deserve a huge “thank you!” from me.

Making an Ember.js Component More Reusable

This is Part 2 of a mini-series on components. Here is the first post in the series:

Convert a view into a component

Intro

We saw how to turn the star-rating view into a component to make it more reusable, and less reliant on its context. Everything that the component needs to do its job had to be passed in, and that is enough for it to be reusable not just across screens in your application but also across different applications. Or is it? Let’s take a look at the component code again:

1
2
3
4
5
6
7
8
9
10
11
12
13
App.StarRatingComponent = Ember.Component.extend({
  classNames: ['rating-panel'],

  fullStars: Ember.computed.alias('item.rating'),
  numStars:  Ember.computed.alias('maxRating'),
  (...)
  actions: {
    setRating: function() {
      var newRating = parseInt($(event.target).attr('data-rating'), 10);
      this.get('item').set('rating', newRating);
      this.sendAction('setAction', this.get('item'));
    }
  }

Is something assumed about the object whose rating our component will display and set? I’ll give you some time to think about it.

A glove that fits all hands

What we assume is that the item that gets passed in has a rating property. If we really want our component to be used in all Ember applications (why not reach for the stars?), then this should not be an assumption that we make. After all, a player in a hockey team might have a score property and not rating. We could get around that by aliasing score to rating in our controller:

1
2
3
App.PlayerController = Ember.ObjectController.extend({
  rating: Ember.computed.alias('score');
});

However, this is inconvenient for the app developer and is only necessary because the star-rating component is not flexible enough. It’s as if I had to reshape my hand to fit the glove.

So let’s make it take the property name as a parameter, too:

<script type="text/x-handlebars" data-template-name="artists/songs">
  {{#each songs}}
    <div class="list-group-item">
      {{title}}
      {{star-rating item=this ratingProperty="rating" maxRating=5 setAction="setRating"}}
    </div>
  (...)
  {{/each}}
</script>

That was easy, now comes the harder part, the component code. Previously, the fullStars property of the component was just an alias for item.rating. We can’t do that anymore, since the name of the rating property is only known when the component is used in a template, and can thus differ in each case.

Did Ember let us down this time? Before, it had kept the fullStars property of our component in sync with the item’s rating. We just sat back and took sips of our mojito. Now, when the going gets tough, we are on our own.

Well, not really. We are doing some advanced stuff so it’s no surprise that we have to use advanced tools that are not needed in the majority of cases. Ember has nice lower-level functions to support us.

We have to set up the property synchronization ourselves but it sounds scarier than it is. We just have to watch when the item’s rating (score, points, etc.) property changes and set the fullStars property to that value:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
App.StarRatingComponent = Ember.Component.extend({
  classNames: ['rating-panel'],

  numStars:  Ember.computed.alias('maxRating'),
  fullStars: null,

  didInsertElement: function() {
    var property = this.get('ratingProperty');
    this.set('fullStars', this.get('item').get(property));
    Ember.addObserver(this.get('item'), property, this, this.ratingPropertyDidChange);
  },

  willDestroyElement: function() {
    var property = this.get('ratingProperty');
    Ember.removeObserver(this.get('item'), property, this.ratingPropertyDidChange);
  },

  ratingPropertyDidChange: function(item, ratingProperty) {
    this.set('fullStars', item.get(ratingProperty));
  },
  (...)
}

There are several things that might be new to you, dear reader, so let me go through each of them.

The most important thing is the call to ‘Ember.addObserver(object, property, context, function)’. Whenever property of object changes, it calls function with context as its this. (Providing a context is optional).

The observer function (ratingPropertyDidChange) gets the object that was changed as its first parameter and the property name that was changed. In this case, it does not have to do anything else but set the fullStars property of the component to the new value of the item’s rating property.

The observer is set up in the didInsertElement function. It is a handy lifecycle-event for Ember views (and thus components) which gets called after the view has been inserted into the DOM. This time, we don’t need it to be in the DOM already but it serves as a convenient way to add the observer.

Lastly, since the observer was added manually, it has to be torn down manually, too, when it is no longer needed. We do this in willDestroyElement, another view lifecycle event which gets called before the element gets removed from the DOM. Also, the code comments mention the following about willDestroyElement:

If you write a `willDestroyElement()` handler, you can assume that your
`didInsertElement()` handler was called earlier for the same element.

This makes didInsertElement - willDestroyElement a perfect pair for manually setting up and tearing down event handlers (or observers) even if no DOM manipulation has to be carried out.

I’ve made a jsbin to show how the star-rating component can now be used with a score property while the component code stays identical:

Reusable Star Rating component

Conclusion

We now have a star-rating component that is general enough to be used in all contexts. Go ahead and use it in your Ember app and let me know if I missed something.

Actually, there are a couple of featurettes -unrelated to its flexibility, as far as I see- we can add which I might come back to.


This was Part 2 of a mini-series on components. Here is the third post in the series:

Readers’ Letters: Making an Ember.js Component Even Better

Convert a View Into a Component

Intro to components in Ember

Components landed in Ember in 1.0.rc6, in June 2013. They are reusable widgets that are built on top of HTML and provide a richer functionality. For the sake of reusability, they are isolated from their surroundings, and -as opposed to views- do not have access to their context. Everything a component has to know from the outside world has to be passed in at creation. Anything it wants to communicate to the outside world needs to be sent via events (or actions, in Ember parlance).

Usability is meant not just between different parts of the same application but across Ember applications, too. Once achieved, it would imply that a component for a specific task needs to be written once and could be used anywhere, just like jQuery plugins.

That is an ambitious goal which is to expected from an ambitious framework. We are not quite there, yet, and the specifics are still under discussion. Nevertheless, components are a great thing and you should start using them today, if you have not already.

“View”.replace(“Component”)

Wherever you would use a component today, you would have used a view before components were possible. Views still have their role in an Ember app but when existing html functionality is enhanced to give a richer, or more complex user interaction and reusability is important, you should reach for components.

In this post, I’m going to show how to swap out an existing view with a component. The example I’m going to use is the star rating view from the Rock & Roll application.

Star rating component

Here is what the star rating view looks like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
App.StarRating = Ember.View.extend({
  classNames: ['rating-panel'],
  templateName: 'star-rating',
  rating: Ember.computed.alias('context.rating'),

  fullStars: Ember.computed.alias('rating'),
  numStars:  Ember.computed.alias('maxRating'),

  stars: function() {
    var ratings = [];
    var fullStars = this.starRange(1, this.get('fullStars'), 'full');
    var emptyStars = this.starRange(this.get('fullStars') + 1, this.get('numStars'), 'empty');
    Array.prototype.push.apply(ratings, fullStars);
    Array.prototype.push.apply(ratings, emptyStars);
    return ratings;
  }.property('fullStars', 'numStars'),

  starRange: function(start, end, type) {
    var starsData = [];
    for (var i = start; i <= end; i++) {
      starsData.push({ rating: i, full: type === 'full' });
    };
    return starsData;
  },
  actions: {
    setRating: function() {
      var newRating = $(event.target).data('rating');
      this.set('rating', newRating);
      App.Adapter.ajax('/songs/' + this.get('context.id'), {
        type: 'PUT',
        context: this,
        data: { rating: newRating }
      }).then(function() {
        console.log("Rating updated");
      }, function() {
        alert('Failed to set new rating');
      });
    }
  }
});

The most important thing about components is that they do not have access to their context so any code that does use it needs to be changed.

Back when I wrote the code, I was, somewhat surprisingly, wise enough to use properties whose semantics reflect the inner operation of the widget, namely fullStars and numStars. Now we can reap the benefits of this foresight, because the entire stars and starRange method can remain untouched. It is only the definition of the fullStars property and the setRating action that need to change.

Let’s quickly sketch up the interface of the component. It will need the item whose rating it sets/displays, the name of the action it sends to the outer world when a new rating is set (setAction) and the maximum number of stars.

Having established that, the code transforms to the following:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
App.StarRatingComponent = Ember.Component.extend({
  classNames: ['rating-panel'],

  fullStars: Ember.computed.alias('item.rating'),

  (...)

  actions: {
    setRating: function() {
      var newRating = parseInt($(event.target).attr('data-rating'), 10);
      this.get('item').set('rating', newRating);
      this.sendAction('setAction', this.get('item'));
    }
  }
});

fullStars is now the rating property of the item (in our case, a song) that was passed in.

When a star is clicked, the setRating action is triggered. Here, again, the rating is updated on the item that was passed in. After that, it sends the action that was passed in as setAction to the controller it was used from, passing along the item it received. That is the aforementioned way of sending messages outside.

(You might wonder what sendAction does. It is a shorthand form of sendAction(this.get('foo'), ...).)

Rendering the component

The template will only have minor modifications made to it. Here is what it looked like in its infancy, back when it was a view:

<script type="text/x-handlebars" data-template-name="star-rating">
  {{#each view.stars}}
    <span {{bind-attr data-rating=rating}}
      {{bind-attr class=":star-rating :glyphicon full:glyphicon-star:glyphicon-star-empty"}}
      {{action "setRating" target=view}}>
    </span>
  {{/each}}
</script>

And here is the shiny, new component form:

<script type="text/x-handlebars" data-template-name="components/star-rating">
  {{#each stars}}
    <span {{bind-attr data-rating=rating}}
      {{bind-attr class=":star-rating :glyphicon full:glyphicon-star:glyphicon-star-empty"}}
      {{action "setRating"}}>
    </span>
  {{/each}}
</script>

The data-template-name of a component needs to start with components and the name of the component needs to have a dash in its name to prevent name collisions with html tags.

The other changes relate to the essence of components, namely that they are not embedded in their context but work in isolation. That is why we both property lookups (in #each stars) and action handlers (action "setRating") both target the component and thus the target does not need to be defined explicitly.

Even more importantly, an action fired from a component’s template will look for that action in the component but will not bubble to the controller (or route). That again enhances the component’s isolation and thus its reusability and shows the care that was made when desinging it.

(Unfortunately, if an action by that name is not found on the component, it will die a silent death which makes debugging more difficult).

Using the component

Now comes that part I love most. Using our polished component is just like calling a function in a language where state is not shared. You pass in everything the component needs to do its bidding and be done with it:

<script type="text/x-handlebars" data-template-name="artists/songs">
  (...)
  {{#each songs}}
    <div class="list-group-item">
      {{title}}
      {{star-rating item=this maxRating=5 setAction="setRating"}}
    </div>
  (...)
  {{/each}}
</script>

Handling the action sent from the component

We saw how the component will send the action passed in as setAction and pass along the item (now: song) with it. We just need to handle it the classic Ember way, either on the controller or the route:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
App.ArtistsSongsRoute = Ember.Route.extend({
  (...)
  actions: {
    setRating: function(song) {
      App.Adapter.ajax('/songs/' + song.get('id'), {
        type: 'PUT',
        data: { rating: song.get('rating') }
      }).then(function() {
        console.log("Rating updated");
      }, function() {
        alert('Failed to set new rating');
      });
    }
  }
});

Observe how the action to update a song’s rating to the backend had to be moved to the route, instead of the view/component where it does not belong. Another win for components.

Don’t get confused by the two different setRating actions. The first is the one defined on the component that gets triggered via the action helper from the component’s template, the second one is the action name that needs to be passed in and has to match the name of the event handler on the route.

Towards better reusability

I hope you got a taste of why components rock and what steps are taken in their design towards their reusability. However, it’s up to writers of components to go all the way and make components general enough to fulfill this promise.

That’s what I’m going to strive for in a later post.


This was Part 1 of a mini-series on components. Here are the subsequent posts in the series:

Part 2: Making an Ember.js Component More Reusable

Part 3: Readers’ Letters: Making an Ember.js Component Even Better

Promises Instead of Callbacks

A few weeks ago I built up a very simple identity map to get rid of a bug in highlighting active links. I introduced promises in order to leverage the fact that Ember.js blocks in model hooks until the model is resolved (or rejected).

In this post, I am taking a step back, and converting all the ajax calls that fetch data from and store data to the backend to use promises. I am also going to extract the most basic adapter that exists, just to do away with the repetition in the XHR calls. Once I have these in place, I will able able to build nice features on top of that.

App.TheMostBasicAdapterThereIs

All calls go to the same backend and talk in json so these can be trivially extracted:

1
2
3
4
5
6
7
App.Adapter = {
  ajax: function(path, options) {
    var options = options || {};
    options.dataType = 'json';
    return Ember.$.ajax('http://rock-and-roll-api.herokuapp.com' + path, options)
  }
}

With that out of the way, we can see where these were used, and replace Ember.$.ajax with App.Adapter.ajax. In the process we are also going to convert the callback-style code to use promises, a worthwhile transformation.

Don’t call me back, promise me you’ll be there

Here is what the code for fetching all the artists from the API looks like after applying the adapter change:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
App.ArtistsRoute = Ember.Route.extend({
  model: function() {
    var artistObjects = [];
    App.Adapter.ajax('/artists', {
      success: function(artists) {
        artists.forEach(function(data) {
          artistObjects.pushObject(App.Artist.createRecord(data));
        });
      }
    });
    return artistObjects;
  },
  (...)
});

Notice that the model initially is an empty array and only when the ajax call returns successfully does that array get filled up and the template rerendered. Not a big deal in itself, but if in a child route we rely on the array containing all the artists (e.g when looking up the identity map or using modelFor), we can be bitten by the async bug. Promises to the rescue.

As I mentioned in the identity map post, if a promise is returned from a model hook, Ember.js will block until the promise is resolved (or rejected). Let’s follow in Ember.js footsteps and convert the above code to return a promise:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
App.ArtistsRoute = Ember.Route.extend({
  model: function() {
    return Ember.RSVP.Promise(function(resolve, reject) {
      App.Adapter.ajax('/artists').then(function(artists) {
        var artistObjects = [];
        artists.forEach(function(data) {
          artistObjects.pushObject(App.Artist.createRecord(data));
        });
        resolve(artistObjects);
      }, function(error) {
        reject(error);
      });
    });
  },
  (...)
});

We wrap the promise returned from the App.Adaptar.ajax call in another promise, which resolves with artist objects instead of the raw data that is returned by the API. In the rejection handler, we pass along any potential error responses by rejecting with the same error that we got.

Next, we do the same thing in the child route. We go from here:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
App.ArtistsSongsRoute = Ember.Route.extend({
  model: function(params) {
    var artist = App.Artist.create();
    App.Adapter.ajax('/artists/' + params.slug, {
      success: function(data) {
        artist.setProperties({
          id: data.id,
          name: data.name,
          songs: App.Artist.extractSongs(data.songs, artist)
        });
      }
    });
    return artist;
  },
  (...)
});

To here:

1
2
3
4
5
6
7
8
9
10
11
12
App.ArtistsSongsRoute = Ember.Route.extend({
  model: function(params) {
    return Ember.RSVP.Promise(function(resolve, reject) {
      App.Adapter.ajax('/artists/' + params.slug).then(function(data) {
        resolve(App.Artist.createRecord(data));
      }, function(error) {
        reject(error);
      });
    });
  },
  (...)
});

To get the “100% promisified” seal, we’ll transform the create calls, too. I’ll only show the one to create an artist since creating a song is the same.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
createArtist: function() {
  var name = this.get('controller').get('newName');

  App.Adapter.ajax('/artists', {
    type: 'POST',
    data: { name: name },
    context: this
  }).then(function(data) {
      var artist = App.Artist.createRecord(data);
      this.modelFor('artists').pushObject(artist);
      this.get('controller').set('newName', '');
      this.transitionTo('artist.songs', artist);
  }, function(reason) {
    alert('Failed to save artist');
  });
}

Here, there is not that much of a difference, the success and error callbacks are replaced by fulfillment and rejection handlers.

The source code with these changes can be got here.

Further studies & posts

You can acquire a deeper knowledge about promises by reading Domenic Denicola’s “You’re missing the point of promises” post and using that as a base for further exploration. Steven Kane’s excellent promise-it-wont-hurt package makes you solve increasingly difficult challenges with promises, which is the best way to learn.

Promisifying all backend calls sets the stage for other routing-related improvements. Stay tuned for more.