javascriptember.jsember-model

Ember.js not updating model


FINAL UPDATE: Kalman Hazins pointed me in the right direction, the computed property wasn't being called because of the fact that it wasn't rendered onscreen, so it wasn't "necessary" to re-compute it. I'll include here the final code for the controller:

App.QuestionController = Ember.ObjectController.extend({
    isEditing : false,

    resetIsEditing : function() {
        this.set('isEditing', false);
    }.observes('model.id'),

    canEditQuestion : function() {
        return this.get('author.id') === App.currentUser;
    }.property('author.id')
}

UPDATE: a "nice enough" solution has been provided by Raymond Liu below , it doesn't really answer the question "why this is happening", but it's the best I've got so far.


I'm learning Ember.js, follwing this book and making some changes. In my controller I have a property bound to the model, but when I change such model the property is not updated.

This is the controller:

App.QuestionController = Ember.ObjectController.extend({
    isEditing : false,

    canEditQuestion : function() {
        this.set('isEditing', false);
        return this.get('author.id') === App.currentUser;
    }.property('model')
});

This is the template:

<script type="text/x-handlebars" id="question">
    {{#if isEditing}}
        <!-- edit question form -->
    {{else}}
        <p id="question">{{question}}</p>
        {{#if canEditQuestion}}
            <a href="#" {{action "toggleEditQuestion"}}>Edit question</a>
        {{/if}}
    {{/if}}
</script>

Note that if I move the {{else}} branch content before {{#if isEditing}} it works as expected (but not how I want to).

Maybe the fact that question is a nested route matters:

App.Router.map(function() {
    this.resource('questions', function() {
        this.resource('question', { path : '/:question_id' });
    });
});

What I want is that even if I'm already editing a question, if I change model the isEditing property should go back to false and the question form is not shown. Apparently I can solve it if I always render the question, but that's not what I want. What am I missing?

EDIT: I'm adding the code for the question and the user model:

App.Question = DS.Model.extend({
    title : DS.attr('string'),
    question : DS.attr('string'),
    date : DS.attr('date'),
    author : DS.belongsTo('user', { async : true }),
    answers : DS.hasMany('answer', { async : true }) 
});

App.User = DS.Model.extend({
    fullname : DS.attr('string'),
    email : DS.attr('string'),
    questions : DS.hasMany('question', { async : true })
});

The App.currentUser property is set in the sign_in controller:

App.SignInController = Ember.Controller.extend({
    needs : [ 'application' ],

    actions : {
        signIn : function() {
            var email = this.get("email");
            var userToLogin = App.User.FIXTURES.findBy("email", email);

            if(userToLogin === void 0) {
                alert("Wrong email!");
                this.set("email", "");
            }
            else {
                localStorage.currentUser = userToLogin.id;
                App.set('currentUser', userToLogin.id);
            }
        }
    }
});

PS: the complete code for my app is available at https://github.com/goffreder/emberoverflow

PS2: I managed to get a functioning jsFiddle of my app. To reproduce you have to sign in using 'tom@dale.com' email. Then, if you click on the first answer, you should see an 'Edit question' link that toggles the question form. Now, with that form opened, if you change question the form will still be availabe, with the new question as content, which is the behaviour I want to avoid.


Solution

  • @goffredder and @Raymond Liu, the reason for canEditQuestion function/property not firing is as follows.

    In Ember.js, the (computed) property evaluation is "magical", but as we all know - "magic" can be expensive. So, the value of a computed property gets cached, so that "magic" doesn't have to happen all the time and unless something changes that cached value doesn't get recomputed. Not only that, if the property is not being shown on the screen - why bother recomputing it? See this jsbin to see exactly what I am talking about. Notice how lazyPropDep never gets updated since lazyDep property is never shown on the screen.

    Now, back to your example. canEditQuestion property is not being rendered to the screen when you are editing the question (since it's in the else block). Therefore, the canEditQuestion function which resets isEditing to false never gets called.

    I think what you need is to place the resetting logic into an observer while leaving the other logic in the property as follows:

    resetIsEditing : function() {
      this.set('isEditing', false);
    }.observes('model.id'),
    
    canEditQuestion : function() {
      return this.get('author.id') === App.currentUser;
    }.property('model'),
    

    This way EVERY TIME a new model (with a new model id) gets loaded into the controller (even if the previous model was in edit mode) the observer code will trigger resetting isEditing property to false