最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Ember.js not updating model - Stack Overflow

programmeradmin0浏览0评论

FINAL UPDATE: Kalman Hazins pointed me in the right direction, the puted property wasn't being called because of the fact that it wasn't rendered onscreen, so it wasn't "necessary" to re-pute 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 plete code for my app is available at

PS2: I managed to get a functioning jsFiddle of my app. To reproduce you have to sign in using '[email protected]' 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.

FINAL UPDATE: Kalman Hazins pointed me in the right direction, the puted property wasn't being called because of the fact that it wasn't rendered onscreen, so it wasn't "necessary" to re-pute 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 plete code for my app is available at https://github./goffreder/emberoverflow

PS2: I managed to get a functioning jsFiddle of my app. To reproduce you have to sign in using '[email protected]' 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.

Share Improve this question edited May 23, 2017 at 12:24 CommunityBot 11 silver badge asked Dec 14, 2014 at 11:07 goffredergoffreder 5034 silver badges18 bronze badges 5
  • Don't you want author.get('id')? In addition to the other points raised in the answers about writing the dependencies correctly. Remember that .property('model') does NOT mean that something IN the model changed, it means that the model itself--the model object--became a different model object. – user663031 Commented Dec 15, 2014 at 16:00
  • EDIT: Sorry wrong target ^_^ Why should I want author.get('id')? It's a better way to access properties? I know that the puted property relies on the whole model to be different, and not a single property of it, it just seems that if I don't render it directly with its template, the model doesn't change. Maybe it's supposed to be this way, I don't know (that's why I'm asking ^_^) – goffreder Commented Dec 15, 2014 at 19:13
  • 1 Agree pletely with @torazaburo on this, definitely specify your dependencies correctly. Also, your template logic doesn't make sense. You want to check isEditing if canEditQuestion is true, not the other way around. Furthermore, you don't want to trigger property changes in puted properties. Observers do this, and properties should only ever be used to pute the property in question. Finally, the puted property is only calculated if it's accessed, so having it nested in a template conditional that starts as false means that it won't ever get updated by the property. – mpowered Commented Dec 18, 2014 at 21:24
  • @AdamRobertson: what do you mean by "specify your dependencies correctly"? Also, I see your point about the template, but if I'm editing I don't want the {{question}} template to be rendered, so I have to check isEditing before. Or am I missing something? – goffreder Commented Dec 18, 2014 at 21:40
  • As @torazaburo mentioned, the dependency is author.id, not model. You also can't specify the dependency for App.currentUser because you're using the global reference to it in the puted property, and I'm pretty sure observers don't work for globals. As for the #if blocks, your final edit should take care of that. I can't emphasize enough to avoid modifying the controller/models in puted property definitions--it's far cleaner and easier to test. – mpowered Commented Dec 18, 2014 at 22:12
Add a ment  | 

4 Answers 4

Reset to default 4

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

In Ember.js, the (puted) property evaluation is "magical", but as we all know - "magic" can be expensive. So, the value of a puted property gets cached, so that "magic" doesn't have to happen all the time and unless something changes that cached value doesn't get reputed. Not only that, if the property is not being shown on the screen - why bother reputing 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

update: I misunderstood your problem. Your want to disable form when go to another question, right? In your question_routs.js file, add this:

App.QuestionRoute = Ember.Route.extend({
  actions: {
    didTransition: function() {
      this.controller.set('isEditing', false);
      return true; // Bubble the didTransition event
    }
  }
})

These code will set 'isEditing' property to false when you transtion to another question. I noticed that the canEditQuestion function/property will not be called when toggle to another question just if isEiting property is set to true, I think that's the very problem need to be solved. And I have no idea why.

It looks as though your puted property depends on two variables, author.id and App.CurrentUser that are not listed in the list of dependent keys. Instead you only listed model as a dependent key. I don't know what the relationship is between author and model, but you should list the actual properties/variables that you need in order to pute your property.

You can do smthing like:

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

评论列表(0)

  1. 暂无评论