2012-04-06 92 views
1

我有以下型号:绑定到嵌套模型Ember.js

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 checklists}} 
    {{this.name}} 
    {{this.remainingItemsCount}} 
{{/each}} 

但是,如果新的checkitem被添加到一个清单,计数不上去。

但是,如果我更改了清单模型中的remainingItemsCount计算属性,使其取决于[email protected],则计数会随着新检查项的添加而递增。

问题是,一旦添加了这个依赖项,子checkitems的集合就会出错 - 它会不断重复第一个checkitem的总checkitems数量(即,如果有5个项目的'isDone'为false ,以及'isDone'为true的四个,则列表数将显示为9,并且第一个checkitem将重复9次)。

我在做什么错?

UPDATE:

事实证明,将依赖于remainingItemsCount属性导致烬数据,使服务器新的呼叫。

没有依赖性,下面XHR请求在网页加载时提出:

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

最后的请求是:

GET http://localhost:3000/checklists 

有了依赖,下面XHR请求在网页加载时做其中包含以下参数,这些参数似乎是第一个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('[email protected]').cacheable() 

...导致烬-data的内置DS.RESTAdapter来调用findMany。 findMany请求应该带有一个ID数组,但是包含一个嵌套在散列内的整个checkitem对象的数组将被传递给它。

SOLUTION

最后,我跟踪的问题为以下观察者深处烬数据:

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整数数组。

回答

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('[email protected]').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 
}));​ 
+0

真正帮助!谢谢。事实证明,添加依赖项会导致ember-data使用表示其中一个checkitems的散列来调用服务器的“/ checkitems”url。我不知道为什么,但这几乎肯定是Checkitems的ArrayProxy出错的原因。 – sirvine 2012-04-06 21:44:13

+0

我不知道这是因为checkitem模型有一个'清单:DS.belongsTo('App.Checklist')'属性。我无法弄清楚为什么服务器会被调用。我从来没有为checkitem DS.Model设置网址。 – sirvine 2012-04-06 21:53:53

+0

但你使用'DS.RESTAdapter'?因为这个人会自动计算你的网址。也许这就是为什么你打电话给你的服务器。 – pangratz 2012-04-06 22:20:51