21
votes
Todos.TodoController = Ember.ObjectController.extend({
  isCompleted: function(key, value){
    var model = this.get('model');

    if (value === undefined) {
      // property being used as a getter
      return model.get('isCompleted');
    } else {
      // property being used as a setter
      model.set('isCompleted', value);
      model.save();
      return value;
    }
  }.property('model.isCompleted')
});

I'm working through the ToDo guide for Ember.js and I can't seem to understand how this controller works. What does the .property() mean? And how come when I remove the 'return value;' line the functionality stays the same. If someone could explain exactly what's going on here that would be great.

Link to the guide: http://emberjs.com/guides/getting-started/marking-a-model-as-complete-incomplete/

2
@RUJordan I still don't understand why you need .property() in this code fullName: function() { return this.get('firstName') + ' ' + this.get('lastName'); }.property('firstName', 'lastName') it still seems redundant to have both a return and a .property() - Wilfred
Also in this piece of code App.SongsController = Ember.ArrayController.extend({ longSongCount: function() { var longSongs = this.filter(function(song) { return song.get('duration') > 30; }); return longSongs.get('length'); }.property('@each.duration') }); You're already returning the length of longSongs so why do you need .property('@each.duration')? - Wilfred

2 Answers

21
votes

In javascript the only way to do some processing when getting or setting one property is using Object.defineProperty:

Object.defineProperty(person, "b", {
  get : function() { 
    return person.firstName + ' ' + person.surname; 
  },
  set : function(newValue) {
    var names = newValue.split(' '); 
    person.firsname = names[0];
    person.surname = names[1]; 
  },
  enumerable : true,
  configurable : true
});

But this have some disadvantages:

  • Isn't cross browser
  • Doesn't have binding, in other words, if firstname or surname changes, the dependent property fullname isn't changed.
  • Calling person.name when person is undefined, make an error to be throwed
  • Isn't possible to trigger observers, no without additional code and aware of the depency hierachy: firstname depends from fullname, and it can be dependency of others properties arghhh!

Due to this Ember has the concept of "property", called computed property.

It can be declared in 2 ways:

foo: Ember.computed(function({
  ...
}).property(dependent keys);

or when using (the default) Ember.ENV.EXTEND_PROTOTYPES = true:

foo: function() {
  ...
}.property(dependent keys);

The property(dependent keys), is needed because it tell to ember what is the properies that when changed, will make the property be updated.

fullname: function(key, value) {
  // setter
  if (value !== undefined) {
    var names = value.split(' ');
    this.set('firstname', names[0]);
    this.set('surname', names[1]);
  }
  // always return the complete result, so nexts calls to this.get('fullname') will return the cached value  
  return this.get('firstname') + ' ' + this.get('surname');
}.property('firstname', 'surname')

Using this, you have the advantage of:

  • when changing firstname or surname to a new value, fullname is changed.
  • The beforeObserves are triggered before changing the value, and the observes are triggered after the value change.
  • Any template referencing some property is updated
  • More then one call to person.get('firstname'), will return a cached value, saving processing. You can disable this using .property(..).volatile()
  • Avoid null or undefined errors, when accessing null objects like: controller.get('person.dog.name') returns undefined, if person or dog is undefined.

I hope it helps

6
votes

.property() marks the function as a computed property so that it can be used in templates, for example, in a consistent way. Is it a JavaScript property foo? Or is it a function foo()? Computed properties solve this issue on all platforms.

The fields and paths passed as arguments are the dependent keys. All computed properties in Ember are cached by default. To know when a computed property needs to be recomputed, Ember needs to know which properties and paths it depends on. In the guide's example, the fullName computed property depends on firstName and lastName; Ember needs to know that so it can recompute fullName when either of those change. (Note: You can turn off caching by using .property().volatile().)

Please read the guides. All of this is documented there.