1

私は次のモデルを持っています:

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}}

ただし、チェックリストに新しいチェック項目が追加された場合、カウントは上がりません。

remainingItemsCountしかし、チェックリスト モデルの計算されたプロパティを に依存するように変更するとcheckitems.@each.done、新しいチェック項目が追加されるとカウントが増加します。

問題は、この依存関係が追加されると、子チェック項目のコレクションが正しくないことです。つまり、チェック項目の総数だけ最初のチェック項目が繰り返されます (つまり、「isDone」が false である項目が 5 つあり、「isDone」が false である項目が 4 つある場合)。 「isDone」が true の場合、リスト カウントは 9 と表示され、最初のチェック項目が 9 回繰り返されます)。

私は何を間違っていますか?

アップデート:

leftItemsCount プロパティに依存関係を追加すると、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モデルが属している属性で定義されているためでしょうか?

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 のハッシュ内にネストされた 1 つの checkitem オブジェクト全体を含む配列が渡されています。

解決

最後に、問題を追跡して、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 整数の配列ではなく、checkitem オブジェクトの配列でした。修正は非常に簡単でした: return store.clientIdForId(association.type, id.id)id 整数の配列を返します。

4

1 に答える 1

2

あなたの説明から 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
}));​
于 2012-04-06T20:46:51.397 に答える