【问题标题】:Binding to a nested model in Ember.js在 Ember.js 中绑定到嵌套模型
【发布时间】:2012-04-20 07:19:48
【问题描述】:

我有以下型号:

App.Checklist = DS.Model.extend({
  name: DS.attr('string'),
  checkitems: DS.hasMany('App.Checkitem', { embedded: true }),

  remainingItemsCount: function() {
    var checkitemsToCount = this.get('checkitems');
    return checkitemsToCount.filterProperty('isDone', false).get('length');
  }.property()

});

我想显示一个清单列表,其中包含每个列表中当前未打开的检查项的计数。

如果我将以下内容放入模板中,我会得到正确的输出:

{{#each checklists}}
  {{this.name}}
  {{this.remainingItemsCount}}
{{/each}}

但是,如果将新的检查项添加到检查表中,则计数不会增加。

但是,如果我更改 Checklist 模型中的 remainingItemsCount 计算属性,使其依赖于 checkitems.@each.done,那么计数会随着新检查项的添加而增加。

问题在于,一旦添加此依赖项,子检查项的集合就会出错 - 它会不断重复第一个检查项的总检查项数(即,如果有五个项目的 'isDone' 为假,和四个 'isDone' 为真,则列表计数将显示为 9,第一个检查项将重复 9 次)。

我做错了什么?

更新:

事实证明,将依赖项添加到 remainingItemsCount 属性会导致 ember-data 对服务器进行新的调用。

在没有依赖的情况下,页面加载时会发出以下 XHR 请求:

GET http://localhost:3000/checklists

通过依赖,页面加载时会发出以下 XHR 请求:

GET http://localhost:3000/checklists
GET http://localhost:3000/checkitems

最后一个请求带有以下参数,这似乎是第一个检查项的表示,包装在“ids”哈希中:

{"ids"=>
  {"0"=>
    {"id"=>"182",
     "checklist_id"=>"4",
     "title"=>
      "Make sure list count automatically increments",
     "is_done"=>"false"}},
 "action"=>"index",
 "controller"=>"checkitems"}

我想知道这是不是因为checkitem 模型是用belongsTo 属性定义的?

App.Checkitem = DS.Model.extend({
  title: DS.attr('string'),
  isDone: DS.attr('boolean'),
  checklist: DS.belongsTo('App.Checklist')
});

更新 2

我仍然不确定为什么,但很明显,将依赖项添加到属性中如下...

remainingItemsCount: function() {
    var checkitemsToCount = this.get('checkitems');
    return checkitemsToCount.filterProperty('isDone', false).length;
}.property('checkitems.@each.isDone').cacheable()

...导致 ember-data 的内置 DS.RESTAdapter 调用 findMany。 findMany 请求应该接受一个 id 数组,而是将一个包含一个完整检查项对象的数组传递给它,该数组嵌套在一个散列中,键为 0。

解决方案

最后,我将问题追溯到 ember-data 深处的以下观察者:

dataDidChange: Ember.observer(function() {
    var associations = get(this.constructor, 'associationsByName'),
        data = get(this, 'data'), store = get(this, 'store'),
        idToClientId = store.idToClientId,
        cachedValue;

    associations.forEach(function(name, association) {
      if (association.kind === 'hasMany') {
        cachedValue = this.cacheFor(name);

        if (cachedValue) {
          var ids = data.get(name) || [];
          var clientIds = Ember.ArrayUtils.map(ids, function(id) {
            return store.clientIdForId(association.type, id);
          });

          set(cachedValue, 'content', Ember.A(clientIds));
          cachedValue.fetch();
        }
      }
    }, this);
  }, 'data')

当观察者到达return store.clientIdForId(association.type, id) 行时,数组ids 是一个检查项对象数组,而不是一个id 整数数组。修复非常简单:return store.clientIdForId(association.type, id.id) 返回一个 id 整数数组。

【问题讨论】:

    标签: javascript ember.js


    【解决方案1】:

    我根据您的描述创建了一个 JSFiddle,但无法重现您的问题。我正在使用 Ember.js 0.9.6 和最新版本的 ember-data,请参阅 http://jsfiddle.net/pangratz666/dGjyR/

    车把

    <script type="text/x-handlebars" data-template-name="checklist" >
        {{#each checklists}}
          {{this.name}}
          remaining: {{this.remainingItemsCount}}
          {{#each checkitems}}
            {{view Ember.Checkbox valueBinding="isDone"}}
          {{/each}}
          <a {{action "addCheckitem"}} class="clickable">add item</a>
          <hr/>
        {{/each}}
    
    </script>​
    

    JavaScript

    App = Ember.Application.create({});
    
    App.Checkitem = DS.Model.extend({
        isDone: DS.attr('boolean')
    });
    
    App.Checklist = DS.Model.extend({
        name: DS.attr('string'),
    
        checkitems: DS.hasMany('App.Checkitem', {
            embedded: true
        }),
    
        remainingItemsCount: function() {
            var checkitemsToCount = this.get('checkitems');
            return checkitemsToCount.filterProperty('isDone', false).get('length');
        }.property('checkitems.@each.isDone').cacheable()
    });
    
    App.store = DS.Store.create({
        revision: 4
    });
    
    App.checklistsController = Ember.ArrayProxy.create({
        content: App.store.find(App.Checklist)
    });
    
    Ember.View.create({
        templateName: 'checklist',
        checklistsBinding: 'App.checklistsController',
        addCheckitem: function(evt) {
            var checklist = evt.context;
            checklist.get('checkitems').addObject(App.Checkitem.createRecord({
                isDone: false
            }));
        }
    }).append();
    
    
    var checklist = App.Checklist.createRecord({
        name: 'firstChecklist'
    });
    
    App.Checklist.createRecord({
        name: 'secondChecklist'
    });
    
    checklist.get('checkitems').addObject(App.Checkitem.createRecord({
        isDone: false
    }));
    checklist.get('checkitems').addObject(App.Checkitem.createRecord({
        isDone: true
    }));
    checklist.get('checkitems').addObject(App.Checkitem.createRecord({
        isDone: true
    }));​
    

    【讨论】:

    • 这真的很有帮助!谢谢。事实证明,添加依赖项会导致 ember-data 调用服务器的“/checkitems” url,其中一个哈希表示一个检查项。我不知道为什么,但这几乎肯定是 checkitems 的 ArrayProxy 出现错误的原因。
    • 我想知道这是不是因为 checkitem 模型有一个checklist: DS.belongsTo('App.Checklist') 属性。我无法弄清楚为什么会调用服务器。我从未为检查项 DS.Model 设置 url。
    • 但是你使用DS.RESTAdapter?因为这会自动计算您的 URL。也许这就是您接到服务器呼叫的原因。
    • 是的,这是使用内置的 DS.RESTAdapter。我会撕开它,看看我是否可以追踪这个问题。也许我需要自己动手。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-29
    • 1970-01-01
    • 2013-09-16
    • 2013-11-04
    • 1970-01-01
    • 2011-04-25
    相关资源
    最近更新 更多