是我从REST端点获得的JSON示例。我需要从subcategories
数组创建一个树状结构,如您所见,它是递归嵌套的。
我现在搜索并尝试了将近一个星期,但是我无法找到完全正常工作的解决方案 - 迄今为止我得到的最好的是子类别作为其子子类别的子项(根据需要)出现,但也作为普通的顶级节点,我显然不想要。
我的问题很简单:我如何对此进行建模,以便ember.js能够创建所需的树状结构?
我不认为这对于经验丰富的余烬用户来说非常困难,但与通常的ember文档一样,它既不是最新的,也不仅仅涵盖了琐碎的概念。
无论我在哪里看,这种嵌套结构的概念似乎都是陌生的(我发现所有嵌套都有不同的类型,但这正是我不需要的)。 JSON格式是不可变的,我必须以它的方式使用它。
我创建了我的项目 - ember-cli 0.2.7 - ember.js 1.13.0 - ember-data 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 - EPS Indesign(相关)
感谢您的帮助。
编辑2015-07-17: 我仍然没有接近动态解决方案,但目前(幸运的是,级别仅限于两个)我很高兴创建一个 subsubcategories模型和sideload。 我遍历有效负载,获取每个子类别(第二级)的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 })
});
subsubcategory:
import DS from 'ember-data';
export default DS.Model.extend({
CategoryName: DS.attr('string'),
hassubcategories: DS.attr('boolean'),
});
也许它可以帮助某些人,也许我甚至可以得到一个真正动态的提示。
答案 0 :(得分:2)
如果你不介意的话,我会在没有显示太多代码的情况下讨论解决方案......:D
在Ember Data(我假设您正在使用)中,商店包含平面集合中所有类型的对象(概念上)。每个对象都由密钥(id
)检索。 Ember数据提供关系属性以模拟hasMany
或belongsTo
关系。因此,要在树结构中连接这些对象,您将从父节点到其子节点有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
),您可以覆盖extractArray
和extractSingle
函数,将JSON有效负载展平为数组,如上所示。我会留下代码来平衡有效载荷作为读者的练习。 :)
您很幸运,您一次获取树中的所有数据,并且不必处理中间REST调用和处理承诺解决方案。这肯定是可行的,但它的异步性使它有点棘手。
一些轻读:
你的下一个问题可能是(对我而言),如何让树中的每个节点都响应自己的URL(/ categories / 61)并在其上下文中显示它的祖先和兄弟。
答案 1 :(得分:0)
作为此问题的更新答案,现在可以通过使用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 Inverses
:
https://guides.emberjs.com/v2.11.0/models/relationships/#toc_reflexive-relations
可以在此处找到EmbeddedRecordsMixin
的文档:
http://emberjs.com/api/data/classes/DS.EmbeddedRecordsMixin.html
这应该允许您的嵌套响应工作,开箱即用Ember Data。这让我觉得很神奇。
希望这有助于某人。
祝你好运。