How I Started to Understand Model Binding Magic in Ember

I’m learning Ember.js in efforts to stay reasonably up to date with client side web technologies. This hasn’t been the easiest going.

Ember’s best feature is that it easily binds values in HTML to their corresponding properties on objects. I’ve found the implementations of this a little confusing sometimes. Going through the Getting Started guide has been helpful, though this part tripped me up for a good hour, specifically this snippet:

Within todos_controller.js
1
2
3
remaining: function() {
  return this.filterBy('isCompleted', false).get('length');
}.property('@each.isCompleted'),

That last line turns the associatied method into a computed property. My understanding of computed properties was as a means to provide a simple call to complex data (eg. a fullName property that joins firstName and lastName properties). This particular method is on a controller, has a previously foreign syntax (@each), and the template that refers to it still uses it’s canonical name (remaining).

Wha?

In order to understand just what was happening, I pumped the brakes and dug into the sample application. My first question was why they were using a computed property altogether. I whipped up another method and added it to my controller:

todos_controller.js
1
2
3
4
5
6
7
remaining: function() {
  return this.filterBy('isCompleted', false).get('length');
}.property('@each.isCompleted'),

derp: function() {
  return 'derp!';
}
index.html
1
<h1>todos {{derp}}</h1>

This resulted in the method body being substituted in the template, which reminded me that {{derp}} was just printing out whatever derp evaluated to, in this case a function.1 So I made derp a computed property:

todos_controller.js
1
2
3
derp: function() {
  return 'derp!';
}.property('what')

This worked. My <h1> was now ‘todos derp!’. In fact, you could simplify it even more by doing this: property() and leaving off a name altogether. It also demonstrated that the first argument to property didn’t have a bearing on what goes in the Template. Next step was to substitute my static text with something useful.

todos_controller.js
1
2
3
derp: function() {
  return this.filterBy('isCompleted', false).get('length');
}.property()

And sure enough, the title was now ‘todos 2’. I suspected this would work because my controller extended Ember.ArrayController, giving it access to filterBy. This lead me back to one of my original questions: what is the purpose of '@each.isCompleted'?

I played around with the todo app a little more and noticed that my {{derp}} was not automatically updating when todos where checked off while the original {{remaining}} was. Then it hit me: '@each' must be a special directive for an ArrayController that bound that function to the isCompleted property of each object in its array. Putting a console.log('called'); confirmed this suspicion — it’s called every time isCompleted is accessed.

After this bit of forensics, things make a little more sense now. I do wish this association was a little more spelled out in the documentation though for us thick-headed developers.

1. This is what JavaScript does. Open the dev tools and log a function without executing it — the body of that function prints out.
Tags: code

Copyright © 2017 - Scott Williams - Powered by Octopress