烬数据:按需加载的hasMany关联 [英] ember-data: Loading hasMany association on demand

查看:243
本文介绍了烬数据:按需加载的hasMany关联的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

(更新了烬数据API版本11 ...)

(Updated for ember-data API Rev 11...)

什么是使用正确的方式<$c$c>DS.Adapter.findAssociation(...)<$c$c>DS.Adapter.findHasMany(...)要按需加载的hasMany 的关联?特别是,一旦你加载子记录,你如何处理与重新加载从服务器父记录清空的hasMany阵列的事实呢?我做的的希望(也许不能)包含的子记录的ID在父的数组。还是有另一种方式来做到这一点,我失踪?

What is the right way to use DS.Adapter.findAssociation(...)DS.Adapter.findHasMany(...) to load hasMany associations on demand? Especially, once you load child records, how do you deal with the fact that re-loading the parent record from the server empties the hasMany array? I do not want to (perhaps can't) include the id's of the child records in an array in the parent. Or is there another way to do this that I'm missing?

作为一个方面说明,我很困惑哪些选项应该在的hasMany / 的belongsTo 定义传递关键联动(和我应该,如果我没有侧载数据或IDS的数组?使用映射),所以,如果你觉得我的问题可能在于我的关联定义,还有你是对的一个很好的机会。

As a side note, I'm confused about what options should be passed in a hasMany/belongsTo definition for key linkage (and am I supposed to use mappings if I have no sideloaded data or array of ids?), so if you think my problem could lie in my association definition, there's a good chance you're right.

我写我自己的 DS.RESTAdapter 的子类来烬数据绑定在某个ASP.NET的WebAPI后端(使用实体框架)。到目前为止好,但我有一个时间让协会的工作权赫克。

I am writing my own subclass of DS.RESTAdapter to tie ember-data to an ASP.NET WebAPI backend (using Entity Framework). So far so good, but I'm having a heck of a time getting associations to work right.

类似这张海报,我注意到,余烬-data的头版<击>说使用的说,如果你在你的模型中的的hasMany 的关联,你 GET 该财产,商店会发出为孩子记录的请求。从页面引用:

Similar to this poster, I noticed that ember-data's front page says used to say that if you have a hasMany association in your model, and you get that property, the store will issue a request for the child records. Quoting from the page:

如果你要求的配置文件,如下所示:

If you were to request the profile, like this:

author.get('profile');


  
  

... REST的适配器将发送到URL /型材的要求?AUTHOR_ID = 1。

…the REST adapter would send a request to the URL /profiles?author_id=1.

言下之意是,这是,如果你不侧向载荷,不包括ID数组会发生什么。我意识到这些文档有点过时了,但我一直没能做到这一点,无论是在API版本7或最近版本9然而<击> 9版本我没有找到 findAssociation 方法在11版还有就是 findHasMany 方法,我猜可能是已经用什么做这一点,和我现在正在尝试使用。

The implication is this is what happens if you don't sideload and don't include an array of ids. I realize that these docs are somewhat out of date, but I haven't been able to make this happen, either in API version 7 or more recently version 9. However in version 9 I did find the findAssociation method, in version 11 there is the findHasMany method, which I'm guessing is what might have been used to make this happen, and which I'm now trying to use.

主要有三个原因,我不希望这样做(也可能无法):

Three main reasons I don't want to do this (and possibly can't):


  1. 这不是明显的如何做的任何这些东西ASP.NET的WebAPI,至少不符合我使用的是简单的基于装饰的方法。而且,我真的很喜欢后端的简单和薄,现在,与EF和的WebAPI它几乎完全样板为每个实体,我完成了!我甚至得到的OData支持过滤自由。

  1. It's not obvious how to do either of these things with ASP.NET WebAPI, at least not with the simple decoration-based approach I'm using. And, I really like the simplicity and thinness of the backend right now, with EF and WebAPI it's almost entirely boilerplate for each entity and I'm done! I even get OData filtering support "free".

我的孩子记录将通过经常查询价格昂贵(骨料......指标汇总,例如)产生。而且有很多不同类型的子实体的一个单亲实体。因此,即使得到了IDS的所有的子类型将是昂贵的,并且产生和侧载所有的子记录都出了问题。

My child records will often be generated via expensive queries (aggregates...metrics rollups, for instance). And there are lots of different classes of child entities for a single parent entity. So even getting the ids for all the child types would be expensive, and generating and sideloading all the child records are out of the question.

我有孩子的实体,其中主键是复​​合键。我还没有看到这样的例子,即使正在烬数据支持/可能的,至少不会与关联交易(例如,你会怎么做ID数组?)。我改变了我的客户端模型,它可以强制组合键成一个单一字符串计算的财产,这样我就可以使用取回保存单个记录找到(...),但同样我不知道如何做到这一点甚至与关联工作。

I have child entities where the primary key is a composite key. I haven't seen an example of this even being supported/possible in ember-data, at least not for dealing with associations (e.g. how would you do an array of ids?). I made a computed property in my client-side model that coerces the composite key into a single string, so I can retrieve a single record from the store using find(...), but again I have no idea how this would even work with an association.

我已经想通了,在API版本<击> 9(以及一些早期版本而不是全部?) 11,我或许可以实施<罢工> DS.Adapter。 findAssociation DS.Adapter.findHasMany 方法来检索的子记录的hasMany 关联。这主要工作,但需要一些体操。这里是我的全身<击> findAssociation findHasMany 方法:

Trying to use findAssociationfindHasMany

I've figured out that in API version 9 (and some earlier versions but not all?) 11, I can perhaps implement the DS.Adapter.findAssociation DS.Adapter.findHasMany method to retrieve the child records of a hasMany association. This mostly works, but requires some gymnastics. Here is my generalized findAssociation findHasMany method:

findHasMany: function (store, record, relationship, ids) {

    var adapter = this;
    var root = this.rootForType(relationship.type);
    var query = relationship.options.query(record);

    var hits = store.findQuery(relationship.type, query);

    hits.on('didLoad', function () {
        // NOTE: This MUST happen in the callback, because findHasMany is in
        // the execution path for record.get(relationship.key)!!! Otherwise causes
        // infinite loop!!!
        var arrMany = record.get(relationship.key);

        if (hits.get('isLoaded')) {
            arrMany.loadingRecordsCount = 1 + hits.get('length') + (typeof arrMany.loadingRecordsCount == "number" ? arrMany.loadingRecordsCount : 0);
            hits.forEach(function (item, index, enumerable) {
                arrMany.addToContent(item);
                arrMany.loadedRecord();
            });
            arrMany.loadedRecord(); // weird, but this and the "1 +" above make sure isLoaded/didLoad fires even if there were zero results.
        }
    });

}

要完成这项工作,我的的hasMany 定义设置了查询选项值,它是在记录一个函数,返回在为孩子们请求的查询字符串参数散列。因为这是一个ASP.NET的WebAPI后端,这将可能是一个OData的过滤器,例如:

To make this work, my hasMany definitions set a query option value which is a function on the record that returns a hash of parameters for the query string in the request for the children. Since this is for a ASP.NET WebAPI backend, this will probably be an OData filter, e.g.:

App.ParentEntity = DS.Model.extend({
    ...
    children: DS.hasMany('App.ChildEntity', {
        query: function (record) {
            return {
                "$filter": "ChildForeignKey eq '" + record.get('id') + "'"
            };
        }
    })
});

其中的窍门是增加了项目的 ManyArray 使用 addToContent(项目),所以父好像它已被编辑记录不会被标记为脏。另一种是,当我取回JSON父记录开始,我必须手动设置的真正该协会名称的密钥(JSON来自服务器的值不没有密钥的话)。即:

One of the tricks is adding the items to the ManyArray using addToContent(item), so that the parent record doesn't get marked "dirty" as if it has been edited. The other is, when I retrieve the JSON for the parent record initially, I have to manually set a value of true for the association name's key (the JSON coming from the server does not have the key at all). I.e.:

    var a = Ember.isArray(json) ? json : [json];
    for (var i = 0; i < a.length; i++) {
        type.eachAssociation(function (key) {
            var meta = type.metaForProperty(key);
            a[i][key] = true;
        });
    }

这听起来坚果,但是这是为什么:如果你看看 DS.Store.findMany 的实施并找到<击> findAssociation findHasMany 被调用时,你会发现:

This sounds nuts, but this is why: If you look at the implementation of DS.Store.findMany and find where findAssociation findHasMany is called, you'll find:

findMany: function(type, ids, record, relationship){
...
if (!Ember.isArray(ids)) {
  var adapter = this.adapterForType(type);
  if (adapter && adapter.findHasMany) { adapter.findHasMany(this, record, relationship, ids); }
  else { throw fmt("Adapter is either null or does not implement `findMany` method", this); }

  return this.createManyArray(type, Ember.A());
}

如果你看一下这条线从灰烬数据内部函数<击> hasAssociation hasRelationship 其中, findMany 被调用时,你会看到什么传递的第二个参数:

And if you look at this line from the ember-data internal function hasAssociation hasRelationship where findMany is called, you'll see what is passed for the 2nd parameter:

relationship = store.findMany(type, ids || [], this, meta);

因此​​,只有这样,才能<击> findAssociation findHasMany 被称为是在JSON的价值的东西那就是truthy,但的的数组 - 我用真正。我想这可能是一个错误/不完整的,或者说我在错误的轨道上的指示 - 如果有人能告诉我这将是巨大的。

So, the only way to get findAssociation findHasMany to be called is to have the value in the JSON be something that is "truthy", but is not an Array--I use true. I'm thinking this is either a bug/incomplete, or an indication that I'm on the wrong track--if someone could tell me which that would be great too.

通过这一切,我可以得到烬数据自动发出对服务器的请求孩子的记录,例如到 http://myserver.net/api/child_entity/$filter=ChildForeignKey EQ'42'和它的作品 - 子记录被装载,而且他们得到关联父记录(顺便说一句,逆的belongsTo 关系被正确填充过,尽管我不会明确地触摸它 - 我不知道在哪里,那是怎么回事发生)。

With all that, I can get ember-data to automatically issue a request to the server for the child records, e.g. to http://myserver.net/api/child_entity/$filter=ChildForeignKey eq '42' and it works--the child records get loaded, and they get associated with the parent record (incidentally, the inverse belongsTo relationship gets populated properly too, despite the fact that I'm not explicitly touching it--I have no idea where or how that is happening).

但是,迅速分解pretty如果我不停止在那里。

But that breaks down pretty quickly if I don't stop there.

所以说,我的孩子记录成功加载到父记录,但然后导航到的所有的父记录被检索的地方(来填充菜单)。由于新加载的父记录没有数组ids并没有什么侧载的,父记录是没有任何再次刷新儿!更糟的是, ManyArray isLoaded 属性保持真正!所以,我甚至无法观察到任何重新加载孩子。

So say I successfully load up the child records into a parent record, but then navigate to a place where all the parent records are retrieved (to populate a menu). Since the newly loaded parent records have no array of ids and nothing is sideloaded, the parent record is refreshed without any children again! Worse, the ManyArray's isLoaded property remains true! So I can't even observe anything to re-load the children.

所以,如果我同时具有显示子值视图屏幕上,它立即向跳越无子记录值。或者,如果我导航回到之一,当 App.store.find('App.ParentEntity',42)被调用,记录从店里装不来的请求服务器,当然它没有子记录。

So if I simultaneously have a view onscreen displaying the child values, it immediately zaps to having no child record values. Or if I navigate back to one, when App.store.find('App.ParentEntity', 42) is called, the record is loaded from the store without a request to the server, and of course it has no child records.

这是暗示#2,我可能要去这个错误的方法。所以...什么是按需加载子记录的正确方法?

This is hint #2 that I probably am going about this the wrong way. So...what is the right way to load child records on demand?

谢谢了!

推荐答案

根据最新的灰烬数据(2013年1月25日的)......这里是我的解决方案,以延迟加载的hasMany关系。我体改 DS.hasMany 并添加了一个方法来 DS.Adapter

Based on the latest Ember Data (as of Jan 25th 2013)... here's my solution to lazy loading hasMany relationships. I modifed DS.hasMany and added a method to DS.Adapter.

我改变了两行 DS.hasMany

DS.hasMany = function(type, options) {
  Ember.assert("The type passed to DS.hasMany must be defined", !!type);
  return (function(type, options) {
    options = options || {};
    var meta = { type: type, isRelationship: true, options: options, kind: 'hasMany' };
    return Ember.computed(function(key, value) {
      var data = get(this, 'data').hasMany,
          store = get(this, 'store'),
          ids, relationship;

      if (typeof type === 'string') {
        type = get(this, type, false) || get(Ember.lookup, type);
      }

      meta.key = key;
      ids = data[key];
      relationship = store.findMany(type, ids, this, meta);
      set(relationship, 'owner', this);
      set(relationship, 'name', key);
      return relationship;
    }).property().meta(meta);
  })(type, options);

};

首先,我添加了对象...

meta.key = key;

...第二,为previously上面提到的,我改变移除了 findMany 通话空数组...

relationship = store.findMany(type, ids || [], this, meta);

...到...

...to...

relationship = store.findMany(type, ids, this, meta);

...让 IDS 中传递给 findMany 未定义

接下来,我添加了一个 didFindHasMany 挂钩 DS.Adapter

Next, I added a didFindHasMany hook to DS.Adapter:

DS.Adapter.reopen({

  /**
   Loads the response to a request for records by findHasMany.

   Your adapter should call this method from its `findHasMany`
   method with the response from the backend.

   @param {DS.Store} store
   @param {subclass of DS.Model} type
   @param {any} payload
   @param {subclass of DS.Model} record the record of which the relationship is a member (parent record)
   @param {String} key the property name of the relationship on the parent record
   */
  didFindHasMany: function(store, type, payload, record, key) {

    var loader = DS.loaderFor(store);

    loader.populateArray = function(references) {
      store.loadHasMany(record, key, references.map(function(reference) { return reference.id; }));
    };

    get(this, 'serializer').extractMany(loader, payload, type);
  }

});

我仿照这个 DS.Adapter didFindQuery 挂钩使用 loadHasMany ,我发现已经在 DS.Store 实施。然后在我的自定义适配器我实现了一个使用以下code在其成功的回调 findHasMany 方法:

I modeled this after the DS.Adapter's didFindQuery hook using the loadHasMany that I found already implemented on the DS.Store. Then in my custom adapter I implemented a findHasMany method that uses the following code in its success callback:

Ember.run(this, function() {
  adapter.didFindHasMany(store, type, response.data, record, key);
});

我还没有广泛的测试这一点,但它似乎是正常工作。通过对余烬数据的code所做的最新的修改来看,在我看来,他们也陆续在其类似于这种方法的东西将在未来的某一时刻被支持的方向移动。或者最起码这是我的希望。

I haven't tested this extensively but it seems to be working correctly. Looking through recent modifications made to ember-data's code, it seems to me that they have slowly been moving in a direction in which something similar to this approach will be supported at some point in the future. Or at the very least that's my hope.

这篇关于烬数据:按需加载的hasMany关联的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆