アップデート
この質問は、1.0 ベータ以前の Ember Data に適用されることに注意してください。URL を介して関係をロードするメカニズムは、1.0 ベータ以降で大幅に変更されました!
しばらく前にもっと長い質問をしましたが、その後ライブラリが変更されたため、もっと簡単なバージョンを質問します。
どのように使用しますDS.Adapter.findHasMany
か? 私はアダプターを作成していてget
、リレーションシップ プロパティのリレーションシップの内容を読み込めるようにしたいのですが、これがその方法のようです。しかし、Ember Data コードを見ると、この関数を呼び出す方法がわかりません (必要に応じてコメントで説明できます)。
バックエンドで、送信する JSON のプロパティ キーに ID の配列を含める簡単な方法はありません。使用しているシリアライザーでは、それを変更するのに適した場所にフックすることはできません。計算コストが高い。
むかしむかし、Ember Data のフロント ページには、この「遅延読み込み」を行う例が示されていました...これは可能ですか、それともロードマップにリストされているように、「部分的に読み込まれたレコードを処理する」ことはできますか? .?
1 月 15 日現在、API リビジョン 11、マスター ブランチを使用しています。
アップデート
さて、以下はほとんどの場合機能します。まず、findHasMany
テスト ケースの実装に基づいて、アダプターで次のメソッドを作成しました。
findHasMany: function(store, record, relationship, details) {
var type = relationship.type;
var root = this.rootForType(type);
var url = (typeof(details) == 'string' || details instanceof String) ? details : this.buildURL(root);
this.ajax(url, "GET", {
success: function(json) {
var serializer = this.get('serializer');
var pluralRoot = serializer.pluralize(root);
var hashes = json[pluralRoot]; //FIXME: Should call some serializer method to get this?
store.loadMany(type, hashes);
// add ids to record...
var ids = [];
var len = hashes.length;
for(var i = 0; i < len; i++){
ids.push(serializer.extractId(type, hashes[i]));
}
store.loadHasMany(record, relationship.key, ids);
}
});
}
上記の前提条件は、シリアライザーに適切に機能するメソッドが必要ですが、ほとんどの場合extractId
、組み込みのメソッドで十分です。RESTAdapter
これは機能しますが、この遅延読み込みアプローチの試みでまだ実際に回避できていない重大な問題が 1 つあります。元のレコードがサーバーからリロードされると、すべてが無駄になります。これを示す最も単純な使用例は、1 つのレコードをロードしてから を取得しhasMany
、後ですべての親レコードをロードする場合です。例えば:
var p = App.Post.find(1);
var comments = p.get('comments');
// ...later...
App.Post.find();
上記のコードのみの場合、何が起こるかというと、Ember Data がレコードを再実体化するときに、レコードに既に値があったことを認識し ( posts/1
)、それを再入力しようとし、別のコード パスをたどります。 1 文字の ID の配列としての JSON ハッシュの URL 文字列。具体的には、JSON から に値を渡しますEmber.EnumerableUtils.map
。これは当然のことながら、文字列の文字を配列メンバーとして列挙します。
したがって、次のように「パッチを当てる」ことでこれを回避しようとしましDS.Model.hasManyDidChange
た。
// Need this function for transplanted hasManyDidChange function...
var map = Ember.EnumerableUtils.map;
DS.Model.reopen({
});
(^気にしないでください、これは本当に悪い考えでした。)
更新 2
親モデルがサーバーから再ロードされたときに、上記の問題を解決するために (少なくとも) もう 1 つのことをしなければならないことがわかりました。URL が 1 文字に分割されていたコード パスはDS.Model.reloadHasManys
. そこで、このメソッドを次のコードでオーバーライドしました。
DS.Model.reopen({
reloadHasManys: function() {
var relationships = get(this.constructor, 'relationshipsByName');
this.updateRecordArraysLater();
relationships.forEach(function(name, relationship) {
if (relationship.kind === 'hasMany') {
// BEGIN FIX FOR OPAQUE HASMANY DATA
var cachedValue = this.cacheFor(relationship.key);
var idsOrReferencesOrOpaque = this._data.hasMany[relationship.key] || [];
if(cachedValue && !Ember.isArray(idsOrReferencesOrOpaque)){
var adapter = this.store.adapterForType(relationship.type);
var reloadBehavior = relationship.options.reloadBehavior;
relationship.name = relationship.name || relationship.key; // workaround bug in DS.Model.clearHasMany()?
if (adapter && adapter.findHasMany) {
switch (reloadBehavior) {
case 'ignore':
//FIXME: Should probably replace this._data with references/ids, currently has a string!
break;
case 'force':
case 'reset':
default:
this.clearHasMany(relationship);
cachedValue.set('isLoaded', false);
if (reloadBehavior == 'force' || Ember.meta(this).watching[relationship.key]) {
// reload the data now...
adapter.findHasMany(this.store, this, relationship, idsOrReferencesOrOpaque);
} else {
// force getter code to rerun next time the property is accessed...
delete Ember.meta(this).cache[relationship.key];
}
break;
}
} else if (idsOrReferencesOrOpaque !== undefined) {
Ember.assert("You tried to load many records but you have no adapter (for " + type + ")", adapter);
Ember.assert("You tried to load many records but your adapter does not implement `findHasMany`", adapter.findHasMany);
}
} else {
this.hasManyDidChange(relationship.key);
}
//- this.hasManyDidChange(relationship.key);
// END FIX FOR OPAQUE HASMANY DATA
}
}, this);
}
});
その追加により、URL ベースの hasManys の使用はほとんど使用可能になりますが、2 つの主な問題が残ります。
まず、逆belongsTo
関係は正しく機能しません。すべて削除する必要があります。これは、ArrayProxies を使用して RecordArray を実行する方法に問題があるように見えますが、複雑です。親レコードがリロードされると、両方の関係が「削除」のために処理されるため、ループが配列を反復処理している間、 belongsTo 関連付け解除コードが同時に配列から項目を削除し、ループがフリークアウトします。これは、アクセスしようとするためです。もはや存在しないインデックス。私はまだこれを理解していません、そしてそれは難しいです。
2 つ目は、しばしば非効率的です。サーバーから hasMany を頻繁にリロードすることになりますが、少なくとも、サーバー側でいくつかのキャッシュ ヘッダーを送信することで、これを回避できる可能性があります。
この質問のソリューションを使用しようとしている人は、上記のコードをアプリに追加することをお勧めします。最終的にどこかに到達する可能性があります。しかし、これを正しく機能させるには、Ember Data で修正する必要があると思います。
これが最終的によりよくサポートされることを願っています。一方では、彼らが進んでいるJSONAPIの方向性は、この種のことは仕様の一部であると明示的に述べています。しかし一方で、Ember Data 0.13 (または rev 12?) はデフォルトのシリアル化された形式を変更したため、これを行うには、URL が*_ids
...と呼ばれる JSON プロパティにある必要がありますchild_object_ids
。この場合、送信する ID です。これは、ID の配列を使用しないことがユースケースのリストの上位にないことを示唆しているようです。これを読んでいる Ember Data 開発者: この機能をサポートしてください!
これに関するさらなる考えを歓迎します!