【问题标题】:ember.js rest JSON recursively nested with same modelember.js 将 JSON 递归嵌套在相同的模型中
【发布时间】:2015-09-19 23:01:10
【问题描述】:

下面是我从 REST 端点获取的 JSON 示例。我需要从subcategories 数组创建一个树状结构,如您所见,它是递归嵌套的。

我搜索并尝试了将近一周,但无法提出一个完全有效的解决方案 - 到目前为止,我得到的最好的结果是子类别既作为其父子类别的子类别出现(根据需要),但也作为普通的顶级节点,我显然不想要。

我的问题很简单:我该如何建模,以便 ember.js 能够创建所需的树状结构?

我认为这对于有经验的 ember 用户来说并不难,但与往常一样, ember 的文档既不是最新的,也没有涵盖更多琐碎的概念。

无论我在哪里看,这种嵌套结构的概念似乎都是陌生的(我发现的所有嵌套都具有不同的类型,但这正是我不需要的)。 JSON 格式是不可变的,我必须按原样使用它。

我创建了我的项目 - ember-cli 0.2.7 - ember.js 1.13.0 - 余烬数据 1.13.4 使用 DS.RESTAdapter 和 DS.RESTSerializer

{
"id": 28,
"hassubcategories": true,
"CategoryName": "By Asset Type",
"subcategories": [
    {
        "id": 29,
        "CategoryName": "Images",
        "hassubcategories": true,
        "subcategories": [
            {
                "id": 30,
                "CategoryName": "Illustrations",
                "hassubcategories": false
            },
            {
                "id": 31,
                "CategoryName": "Pictures",
                "hassubcategories": true,
                "subcategories": [
                    {
                        "id": 61,
                        "CategoryName": "BMP",
                        "hassubcategories": false
                    },
                    {
                        "id": 32,
                        "CategoryName": "EPS",
                        "hassubcategories": false
                    }
                ]
            }
        ]
    },
    {
        "id": 73,
        "CategoryName": "InDesign  (related)",
        "hassubcategories": false
    }
]

}

这应该在模板中呈现为树状列表 图片 - 插图 -- BMP -- 每股收益 设计(相关)

感谢您的帮助。

编辑 2015-07-17: 我仍然没有接近动态解决方案,但目前(谢天谢地,级别仅限于两个)我很高兴创建一个 子类别模型和侧载。 我遍历有效载荷,获取每个子类别(第 2 级)的 id 并将该子类别移动到列表中。 我的序列化程序子类别:

import DS from 'ember-data';
import ember from 'ember';

export default DS.RESTSerializer.extend({
    isNewSerializerAPI: true,
    primaryKey: 'id',
    normalizeHash: {
    subcategories: function(hash) {
        if(ember.get(hash, 'hassubcategories') && !ember.isEmpty(ember.get(hash, 'subcategories'))) {
            var ids = [];
            ember.get(hash, 'subcategories').forEach(function(cat){
                ids.push(ember.get(cat, 'id'));
            });
            hash.children = ids;
        }

        return hash;
    }
    },
    extractMeta: function(store, type, payload) {
        if (payload && payload.subcategories) {

            var subs = [];
            var subs2 = [];

            payload.subcategories.forEach(function(cat){
                if(cat['hassubcategories']) {
                    var subsubs = cat['subcategories'];
                    subs.addObject(cat);
                    subs2.addObjects(subsubs);
                } else {
                    subs.addObject(cat);
                }
            });

            payload.subcategories = subs;
            payload.subsubcategories = subs2;
        }

        delete payload.id;  // keeps ember data from trying to parse "id" as a record (subcategory)
    delete payload.hassubcategories;  // keeps ember data from trying to parse "hassubcategories" as a record (subcategory)
    delete payload.CategoryName;  // keeps ember data from trying to parse "CategoryName" as a record (subcategory)
    delete payload.ParentID;  // keeps ember data from trying to parse "ParentID" as a record (subcategory)
    }
});

和模型 子类别:

import DS from 'ember-data';

export default DS.Model.extend({
    CategoryName: DS.attr('string'),
    hassubcategories: DS.attr('boolean'),
    children: DS.hasMany('subsubcategories', {async: false })
});

子子类别:

import DS from 'ember-data';

export default DS.Model.extend({
    CategoryName: DS.attr('string'),
    hassubcategories: DS.attr('boolean'),
});

也许它对某人有帮助,也许我什至得到了如何真正动态地实现它的提示。

【问题讨论】:

    标签: javascript json rest recursion ember.js


    【解决方案1】:

    如果你不介意的话,我会在不显示太多代码的情况下讨论解决方案... :D

    在 Ember Data(我假设您正在使用)中,存储包含平面集合中某个类型的所有对象(从概念上讲)。每个对象都通过键 (id) 检索。 Ember Data 提供关系属性来建模hasManybelongsTo 关系。因此,要将这些对象连接到树结构中,您将有一个从父节点到其子节点的hasMany

    App.Category = Ember.Model.extend({
        name: DS.attr(),
        subcategories: DS.hasMany('category'),
        parent: DS.belongsTo('category')
    });
    

    因此,在上面的 JSON 示例中,假设您有这样的类别模型记录:

    [{
      id: 28,
      name: "By Asset Type",
      subcategories: [ 29, 73 ],
      parent: null
    }, {
      id: 29,
      name: "Images",
      subcategories: [ 30, 31 ],
      parent: 28
    }, {
      id: 73,
      name: "InDesign (related)",
      subcategories: [],
      parent: 28
    }, {
    // ... etc ...
    

    所以你可以从这个对象数组中看到,它不是物理嵌套的,而是通过 ID 逻辑嵌套的。 如果你的 JSON 看起来像这样,它可以由 RESTAdapter 处理,一切都很好。 (我希望此时您同意并点头)。问题是,正如您所说,您的 JSON 是“不可变的”,我认为这意味着格式不会改变。

    Serializer 来救援!通过创建自定义CategorySerializer(扩展RESTSerializer),您可以覆盖extractArrayextractSingle 函数以将JSON 有效负载扁平化为一个数组,如上所示。我将把扁平化有效负载的代码留给读者作为练习。 :)

    您很幸运,您可以一次获取树中的所有数据,而不必处理中间 REST 调用和处理承诺解析。这当然是可行的,但它的异步特性使它有点棘手。

    一些简单的阅读:

    您的下一个问题可能是(就像我一样),如何使树中的每个节点响应其自己的 URL (/categories/61) 并在其祖先和兄弟姐妹的上下文中显示它。

    【讨论】:

    • 感谢 steve,但我真的不明白嵌套(即父子关系)是如何在 ember-data 中实现的。所以,这个问题真的在你的最后一句话中描述了。
    • extractArray 中展平数组的好方法是什么?
    【解决方案2】:

    作为此问题的更新答案,现在可以通过将 DS.JSONSerializer 扩展为 DS.EmbeddedRecordsMixin 来获得。

    例如,一个 category 模型包含具有无限嵌套类别的 subcategories 数组,如下所示:

    // app/model/category.js
    import Ember from "ember";
    import Model from "ember-data/model";
    import attr from "ember-data/attr";
    import { belongsTo, hasMany } from 'ember-data/relationships';
    
    export default Model.extend({
      name: attr(),
      subcategories: hasMany('category', { inverse: 'parent' }),
      parent: belongsTo('category', { inverse: 'subcategories' })
    });
    

    您的application 适配器应该与此类似:

    // app/adapters/application.js
    import DS from "ember-data";
    import config from "../config/environment";
    import Ember from "ember";
    
    export default DS.RESTAdapter.extend({
    
    });
    

    您的application 序列化程序应该类似于:

    // app/serializer/application.js
    import DS from 'ember-data';
    
    export default DS.JSONSerializer.extend({
    
    });
    

    最后,你的category 序列化器是这样的:

    // app/serializers/category.js
    import ApplicationSerializer from './application';
    import DS from 'ember-data';
    export default ApplicationSerializer.extend(DS.EmbeddedRecordsMixin,{
      attrs: {
        subcategories: {
          serialize: 'records',
          deserialize: 'records'
        }
      }
    });
    

    您的模型的此设置在此处记录为Reflexive Inverseshttps://guides.emberjs.com/v2.11.0/models/relationships/#toc_reflexive-relations

    EmbeddedRecordsMixin 的文档可以在这里找到: http://emberjs.com/api/data/classes/DS.EmbeddedRecordsMixin.html

    这应该可以让您的嵌套响应与 Ember Data 一起工作。这对我来说很神奇。

    希望这对某人有所帮助。

    祝你好运。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-04-20
      • 1970-01-01
      • 2012-02-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-05-02
      • 2013-11-04
      相关资源
      最近更新 更多