Sunday, 19 October 2014

Ember Objects, part 1

Ember Objects part 1

At the heart of Ember.js is ember objects, they're behind just about everything in the framework. If you're looking to improve your Ember know-how, understanding the Ember.Object is essential.
Ember.Object enables us to define new classes to work with, eg:
var Person = Ember.Object.extend({
    say: function(thing){
        alert(thing);
    }
});
We can also create a sub-class from any existing class by calling extend thus, our Person class above is simply a sub-class of Ember.Object.
This is reflected in Ember when we create our new models, we extend the existing Model class:
App.Person = DS.Model.extend({
  firstName: DS.attr('string'),
  birthday:  DS.attr('date')
});
If we wanted to create new instances of our Person object we utilise create instead, like so:
var tom = Person.create();
And then to create new instances of our model, we call createRecord, this is because a few extra bits are going on under the hood.

Get Set

If you're familiar with Ruby, you can override existing class methods here too, and like Ruby, we can access the underlying implementation of those class methods with a call to _super().
Ember objects make use of get and set as opposed to other frameworks for accessing and writing to its properties. It uses these methods to get around performance issues with multiple DOM updates and bindings. 
For example, when set is called, Ember will check to see if the value we're setting our property to is different to the existing value. If so, Ember knows to go ahead and trigger any updates to bindings, observers or computed properties. 
One additional feature with get is that it gives us access to unknownProperty, this is kind of similar to method_missing in Ruby, in that it allows our object to respond to requests it hasn't explicitly defined. 

Computed Properties

Computed properties are one of the major selling points of Ember. They can be setup like so:
var Room = Ember.Object.extend({
  width: null,
  height: null,
  area: function() {
    return this.get('width') * this.get('height');
  }.property('width', 'height')
});

var study = Room.create({
    width: 50,
    height: 10,
});

study.get('area') // 500

study.set('width', '10');

study.get('area') // 100

In the above example, we create a Room object that has a computed property of area, this computed property depends on width and height, so if one of those attributes changes, the value of area changes as a result.
Additionally, as opposed to say Angular, it also means in our views we can simply use the following:
{{#each}}
    Height: {{height}}
    Width: {{width}}
    Area: {{area}}
{{/each}}

No need for area(), which makes it much easier to update your application, should you require a computed property rather than an attribute.
You can also declare computed properties with some handy shorthand macros:

var Stark = Ember.Object.extend({
  winterIsComing: Ember.computed.equal('snow', 'ice'),

  direWolf: Ember.computed.and('wolf', 'large'),

  knowNothing: Ember.computed.not('knowledge'),

  knowNothing: Ember.computed.gte('bananas.length', 2),

  alive: Ember.computed.or('notFanFavourite', 'onTheWall')
});

You additionally don't have to supply the property declaration, which can make these easier to write. 
For more of these, see the documentation at Ember.js
So far we've covered how to create objects and instantiate them, how to work with attributes and computed properties. In the next post we'll be looking at Observers, some more complex computed property methods and bindings.