我有以下型号:
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}}
但是,如果将新的checkitem添加到核对清单中,则计数不会增加。
但是,如果我更改了清单模型中的remainingItemsCount
计算属性,使其依赖于checkitems.@each.done
,则计数会随着新的复选项的添加而递增。
问题是,一旦添加了此依赖项,子项目的集合是错误的 - 它会不断重复第一个checkitem以查找总的checkitems数量(即,如果有五个项目'isDone'为false,对于'isDone'为真的四个,则列表计数将显示为9,并且第一个checkitem将重复9次)。
我做错了什么?
更新
事实证明,将依赖项添加到remainingItemsCount属性会导致ember-data对服务器进行新的调用。
如果没有依赖关系,则会在页面加载时发出以下XHR请求:
GET http://localhost:3000/checklists
使用依赖项时,会在页面加载时发出以下XHR请求:
GET http://localhost:3000/checklists
GET http://localhost:3000/checkitems
最后一个请求带有以下参数,这些参数似乎是第一个checkitem的表示形式,包含在“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数组,而是一个包含一个完整的checkitem对象的数组,该对象嵌套在一个带有键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
是一个checkitem对象数组,而不是id整数数组。修复非常简单:return store.clientIdForId(association.type, id.id)
返回一个id整数数组。
答案 0 :(得分:2)
我根据您的描述创建了一个JSFiddle,无法重现您的问题。我使用的是Ember.js 0.9.6和最新版本的余烬数据,请参阅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
}));