我的适配器使用findHasMany
加载hasMany
关系的子记录。
我的findHasMany
适配器方法直接基于test case for findHasMany
。它根据需要检索hasMany
的内容,并最终执行以下两个操作:
store.loadMany(type, hashes);
// ...
store.loadHasMany(record, relationship.key, ids);
(如果出现问题,findHasMany
的完整代码如下,但我不这么认为。)
真正奇怪的行为是:它似乎在loadHasMany
(或某些后续异步过程)中的某个地方只有第一个和最后一个子记录获得其反belongsTo
属性集,即使所有子记录都添加到hasMany
方。即,如果posts/1
有10条评论,这就是我在所有内容加载后得到的结果:
var post = App.Posts.find('1');
post.get('comments').objectAt(0).get('post'); // <App.Post:ember123:1>
post.get('comments').objectAt(1).get('post'); // null
post.get('comments').objectAt(2).get('post'); // null
// ...
post.get('comments').objectAt(8).get('post'); // null
post.get('comments').objectAt(9).get('post'); // <App.Post:ember123:1>
我的适配器是DS.RESTAdapter
的子类,我认为我的适配器或序列化程序中的任何内容都不会导致这种行为。
以前有人见过这样的事吗?虽然有人可能知道为什么会发生这种情况,但这很奇怪。
使用findHasMany
只允许我在访问属性时加载hasMany
的内容(在我的情况下很有价值,因为计算ID数组会很昂贵)。所以说我有经典的帖子/评论示例模型,服务器返回帖子/ 1:
{
post: {
id: 1,
text: "Linkbait!"
comments: "/posts/1/comments"
}
}
然后我的适配器可以按需检索/posts/1/comments
,如下所示:
{
comments: [
{
id: 201,
text: "Nuh uh"
},
{
id: 202,
text: "Yeah huh"
},
{
id: 203,
text: "Nazi Germany"
}
]
}
以下是我的适配器中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);
var query = relationship.options.query ? relationship.options.query(record) : {};
this.ajax(url, "GET", {
data: query,
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);
}
});
}
答案 0 :(得分:1)
通过在您的应用中插入以下代码来覆盖DS.RelationshipChange.getByReference
方法:
DS.RelationshipChange.prototype.getByReference = function(reference) {
var store = this.store;
// return null or undefined if the original reference was null or undefined
if (!reference) { return reference; }
if (reference.record) {
return reference.record;
}
return store.materializeRecord(reference);
};
是的,这会覆盖Ember Data中的私有内部方法。是的,任何更新都可能随时中断。我很确定这是Ember Data中的一个错误,但我并不是100%肯定这是正确的解决方案。但它确实解决了这个问题,并可能解决其他与关系有关的问题。
此修复程序旨在于2013年4月29日起应用于Ember Data master。
DS.Store.loadHasMany
调用DS.Model.hasManyDidChange
,它会检索所有子记录的引用,然后set
将hasMany的content
引用到引用数组。这将启动一系列观察者。最终调用DS.ManyArray.arrayContentDidChange
,其中第一行是this._super.apply(this, arguments);
,调用超类方法Ember.Array.arrayContentDidChange
。 Ember.Array
方法包含一个优化,用于缓存数组中的第一个和最后一个对象,并仅在这两个数组成员上调用objectAt
。所以有一个部分可以选择第一个和最后的记录。
接下来,由于DS.RecordArray
实施objectAtContent
方法(来自Ember.ArrayProxy
),objectAtContent
实施会调用DS.Store.recordForReference
,而DS.Store.materializeRecord
会调用record
}。 最后一项功能会将DS.ManyArray.arrayContentDidChange
属性添加到作为副作用传入的引用中。
现在我们得到了我认为的错误。在DS.RelationshipChangeAdd
中,在调用超类方法之后,它遍历所有新引用并创建一个封装所有者和子记录引用的var reference = get(this, 'content').objectAt(i);
实例。但循环内的第一行是:
objectAt
与上一条和最后一条记录不同,这会直接在Ember.NativeArray
上调用ArrayProxy
并绕过objectAtContent
方法,包括DS.Store.materializeRecord
挂钩,这意味着record
- 在引用对象上添加DS.RelationshipChangeAdd.sync
属性 - 可能从未在某些引用上调用过。
接下来,在循环中创建的关系更改紧接着(在相同的运行循环中)应用此调用树:DS.RelationshipChange.getFirstRecord
- &gt; DS.RelationshipChange.getByReference
- &gt; record
。 最后一个方法希望引用对象具有record
属性。但是,DS.Store.materializeRecord
属性仅在第一个和最后一个引用对象上设置,原因如上所述。因此,除了第一个和最后一个记录之外的所有记录都无法建立关系,因为它无法访问子记录对象!
只要引用上不存在record
属性,上述修复就会调用var store = this.store;
。函数的最后一行是唯一添加的内容。一方面,看起来就像这是初衷:原始中的content
行声明了一个在函数中没有使用的变量,那么它的用途是什么?此外,如果没有添加的行,该函数并不总是返回一个值,这对于预期会这样做的函数来说有点不寻常。另一方面,这可能导致大规模物化,在某些情况下这是不可取的(但是,在某些情况下,如果没有它,这种关系就不会起作用了。)
我提到的“观察者链”采取了一些奇怪的道路。发起事件是在DS.ManyArray
上设置Ember.ArrayProxy
属性,扩展content
- 因此arrangedContent
属性具有从属属性arrangedContent
。重要的是,content
上的观察者在执行Ember.propertyDidChange
上的观察员之前执行(请参阅Ember.ArrayProxy.arrangedContentArrayDidChange
)。但是,Ember.Array.arrayContentDidChange
的默认实现只调用DS.ManyArray
,Ember.ManyArray.arrayContentDidChange
实现! 关键是,这看起来像某些代码以非预期的顺序执行的配方。也就是说,我认为record
可能比预期更早执行。如果是这种情况,那么期望content
属性已经存在于所有引用上的上述代码可能已经合理地期望这一点,因为直接在DS.Store.materializeRecord
属性上的观察者之一可能会调用{{ 1}}在每个引用上。但我没有深入挖掘,以确定这是否属实。