Ember-Data:如何使用`DS.Adapter.findHasMany`

S'p*_*'Kr 10 ember.js ember-data

UPDATE

请注意,此问题适用于Ember Data pre-1.0 beta,通过URL加载关系的机制在1.0 beta之后发生了显着变化!


我回过头问了一个更长的问题,但自从那时候图书馆发生了变化,我会问一个更简单的版本:

你怎么用的DS.Adapter.findHasMany?我正在构建一个适配器,我希望能够加载get关系属性的关系内容,这看起来像是这样做的方式.但是,看看Ember数据代码,我看不出如何调用这个函数(如果需要,我可以在评论中解释).

我的后端没有一种简单的方法可以在我发送的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);
        }
    });
}
Run Code Online (Sandbox Code Playgroud)

上面的先决条件是你必须extractId在序列化程序中有一个良好的工作方法,但RESTAdapter在大多数情况下,内置的方法可能会.

这是有效的,但有一个重要的问题,我在这种懒惰加载方法的任何尝试中还没有真正得到:如果原始记录从服务器重新加载,一切都进入底池.显示这一点的最简单的用例是,如果您加载单个记录,然后检索hasMany,然后加载所有父记录.例如:

var p = App.Post.find(1);
var comments = p.get('comments');
// ...later...
App.Post.find();
Run Code Online (Sandbox Code Playgroud)

仅在上面的代码的情况下,当Ember Data重新实现记录时,它会识别记录(posts/1)上已经有一个值,尝试重新填充它,并遵循一个不同的代码路径来处理JSON哈希中的URL字符串作为单字符ID的数组.具体来说,它将值从JSON传递给Ember.EnumerableUtils.map,可以理解地将字符串的字符枚举为数组成员.

因此,我尝试通过"修补"来解决这个问题DS.Model.hasManyDidChange,如果发生这种情况,就像这样:

// Need this function for transplanted hasManyDidChange function...
var map = Ember.EnumerableUtils.map;

DS.Model.reopen({

});
Run Code Online (Sandbox Code Playgroud)

(^没关系,这是一个非常糟糕的主意.)

更新2

当我从服务器重新加载父模型时,我发现我必须做(至少)一件事来解决上面提到的问题.将URL拆分为单个字符的代码路径位于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);
  }

});
Run Code Online (Sandbox Code Playgroud)

通过这种添加,使用基于URL的hasManys 几乎可用,还有两个主要问题:

首先,反向belongsTo关系无法正常工作 - 您必须将它们全部删除.这似乎是使用ArrayProxies完成RecordArrays的方式的一个问题,但它很复杂.当父记录被重新加载时,两个关系都被处理为"删除",所以当循环遍历数组时,belongsTo解除关联代码同时从数组中删除项目,然后循环因为尝试访问而变得怪异一个不再存在的索引.我还没想出这个,这很难.

其次,它通常是低效的 - 我最终经常从服务器重新加载hasMany ......但至少我可以通过在服务器端发送一些缓存头来解决这个问题.

任何人都试图在这个问题中使用这些解决方案,我建议你将上面的代码添加到你的应用程序中,它最终会让你到达某个地方.但我认为这真的需要在Ember Data中修复才能正常工作.

我希望最终得到更好的支持.一方面,他们明确指出的JSONAPI方向说这种事情是规范的一部分.但另一方面,Ember Data 0.13(或rev 12?)改变了默认的序列化格式,因此如果你想这样做,你的URL必须在一个叫做的JSON属性中*_ids...例如child_object_ids...当它不均匀时你在这种情况下发送的身份证!这似乎表明,在用例列表中不使用ID数组并不高.任何Ember Data开发者都会这样说:请支持这个功能!

欢迎进一步思考!

Cyr*_*uck 3

有效负载需要包含数组以外的“其他内容”,而不是 ID 数组。

对于 RESTAdapter,返回的 JSON 如下所示:

{blog: {id: 1, comments: [1, 2, 3]}
Run Code Online (Sandbox Code Playgroud)

如果你想手动/以不同的方式处理关联,你可以返回一个像这样的 JSON:

{blog: {id: 1, comments: "/posts/1/comments"}
Run Code Online (Sandbox Code Playgroud)

然后由您的适配器从指定的 URL 获取数据。

请参阅相关测试:https://github.com/emberjs/data/blob/master/packages/ember-data/tests/integration/has_many_test.js#L112