musically_ut
musically_ut

Reputation: 34288

How to observe embedded arrays in Ember objects?

In Ember.js, creating a observer which reacts to changes in sub-properties which is an array leads to non intuitive behavior. If the observer directly observes the array property, it fails to fire while if it observes a binding to the same property, it works as expected.

A simple example:

Handlebars:

<script type="text/x-handlebars">
    <b>App.View1.name:</b> {{App.View1.name}} <br />
    <b>App.View2.name:</b> {{App.View2.name}}
</script>

Javascript:

App = Em.Application.create();

// A common dependency for two views
var dependency = Em.Object.create({
    prop: []
});

// Directly observes dependency.prop
App.View1 = Em.View.create({
    dependency: dependency,
    name: "Foo",
    changeName: function () {
        this.set("name", "Bar");
    }.observes("dependency.prop")
});

// Indirectly observes dependency.prop via a binding
App.View2 = Em.View.create({
    dependency: dependency,
    name: "Foo",
    changeName: function () {
        this.set("name", "Bar");
    }.observes("prop"),
    propBinding: "dependency.prop"
});

// Updating the 'prop' property of dependency with an element.
dependency.get("prop").pushObject("An object.");

I have put it up on JSFiddle here: http://jsfiddle.net/kbaXG/50/

Here, App.View1.name is NOT updated (stays "Foo") while App.View2.name is updated (to "Bar").

Is this expected behavior and is creating a binding an acceptable workaround?

Upvotes: 1

Views: 2307

Answers (1)

pangratz
pangratz

Reputation: 16163

Bindings is a fundamental concept in Ember.js, so don't hesitate to use it :)

Your example works if you create a binding for your dependency, see http://jsfiddle.net/pangratz666/Gqc7Q/:

App = Em.Application.create({
    VERSION: "0.1"
});

App.dependency = Em.Object.create({
    prop: []
});

App.View1 = Em.View.create({
    dependencyBinding: 'App.dependency',
    name: "Foo",
    changeName: function () {
        this.set("name", "Bar");
    }.observes("dependency.prop")
});

App.View2 = Em.View.create({
    dependencyBinding: 'App.dependency',
    name: "Foo",
    changeName: function () {
        this.set("name", "Bar");
    }.observes("prop"),
    propBinding: "dependency.prop"
});

// Updating the 'prop' property of dependency.
dependency.get("prop").pushObject("An object.");

UPDATE

The problem was that you are changing the contents of an array and not the array itself. So you have to change the observer path to dependency.prop.@each, see http://jsfiddle.net/pangratz666/3QccA/. But I don't know why it's working with the binding approach and not with this one... You should file a ticket on GitHub.

As a last note, the above solution using bindings is the approach which is more Ember like ...

var dependency = Em.Object.create({
    prop: []
});

App.View1 = Em.View.create({
    dependency: dependency,
    name: "Foo",
    changeName: function () {
        this.set("name", "Bar");
    }.observes("dependency.prop.@each")
});

App.View2 = Em.View.create({
    dependency: dependency,
    name: "Foo",
    changeName: function () {
        this.set("name", "Bar");
    }.observes("prop"),
    propBinding: "dependency.prop.@each"
});

// Updating the 'prop' property of dependency.
dependency.get("prop").pushObject("An object.");

Upvotes: 2

Related Questions