骨干中的嵌套模型.js,如何接近
Nested Models in Backbone.js, how to approach
我从服务器提供了以下JSON。有了这个,我想创建一个具有嵌套模型的模型。我不确定哪种方法是实现这一目标。
//json
[{
name : "example",
layout : {
x : 100,
y : 100,
}
}]
我希望将它们转换为具有以下结构的两个嵌套主干模型:
// structure
Image
Layout
...
所以我像这样定义布局模型:
var Layout = Backbone.Model.extend({});
但是,我应该使用以下两种(如果有(技术中的哪一种来定义图像模型?A还是B?
一个
var Image = Backbone.Model.extend({
initialize: function() {
this.set({ 'layout' : new Layout(this.get('layout')) })
}
});
或,B
var Image = Backbone.Model.extend({
initialize: function() {
this.layout = new Layout( this.get('layout') );
}
});
编写 Backbone 应用程序时遇到了同样的问题。必须处理嵌入式/嵌套模型。我做了一些我认为非常优雅的解决方案的调整。
是的,您可以修改 parse 方法来更改对象中的属性,但所有这些实际上是非常难以维护的代码 IMO,感觉更像是一种黑客而不是解决方案。
这是我对你的例子的建议:
首先像这样定义布局模型。
var layoutModel = Backbone.Model.extend({});
然后这是您的图像模型:
var imageModel = Backbone.Model.extend({
model: {
layout: layoutModel,
},
parse: function(response){
for(var key in this.model)
{
var embeddedClass = this.model[key];
var embeddedData = response[key];
response[key] = new embeddedClass(embeddedData, {parse:true});
}
return response;
}
});
请注意,我没有篡改模型本身,而只是从 parse 方法传回所需的对象。
这应该确保从服务器读取时嵌套模型的结构。现在,您会注意到此处实际上没有处理保存或设置,因为我觉得使用正确的模型显式设置嵌套模型对您来说是有意义的。
这样:
image.set({layout : new Layout({x: 100, y: 100})})
另请注意,您实际上是通过调用以下内容来调用嵌套模型中的 parse 方法:
new embeddedClass(embeddedData, {parse:true});
您可以根据需要在model
字段中定义任意数量的嵌套模型。
当然,如果您想将嵌套模型保存在其自己的表中。这还不够。但是在读取和保存整个对象的情况下,这个解决方案应该就足够了。
我发布此代码作为Peter Lyon建议重新定义parse的示例。 我有同样的问题,这对我有用(使用 Rails 后端(。 这段代码是用 Coffeescript 编写的。 我为不熟悉它的人明确了几件事。
class AppName.Collections.PostsCollection extends Backbone.Collection
model: AppName.Models.Post
url: '/posts'
...
# parse: redefined to allow for nested models
parse: (response) -> # function definition
# convert each comment attribute into a CommentsCollection
if _.isArray response
_.each response, (obj) ->
obj.comments = new AppName.Collections.CommentsCollection obj.comments
else
response.comments = new AppName.Collections.CommentsCollection response.comments
return response
或者,在 JS 中
parse: function(response) {
if (_.isArray(response)) {
return _.each(response, function(obj) {
return obj.comments = new AppName.Collections.CommentsCollection(obj.comments);
});
} else {
response.comments = new AppName.Collections.CommentsCollection(response.comments);
}
return response;
};
使用来自骨干关联的Backbone.AssociatedModel
:
var Layout = Backbone.AssociatedModel.extend({
defaults : {
x : 0,
y : 0
}
});
var Image = Backbone.AssociatedModel.extend({
relations : [
type: Backbone.One,
key : 'layout',
relatedModel : Layout
],
defaults : {
name : '',
layout : null
}
});
我不确定 Backbone 本身是否有推荐的方法。 布局对象在后端数据库中是否具有自己的 ID 和记录? 如果是这样,您可以像现在一样将其设置为自己的模型。 如果没有,您可以将其保留为嵌套文档,只需确保在 save
和 parse
方法中将其正确转换为 JSON 和从 JSON 转换即可。 如果您最终采用这样的方法,我认为您的 A 示例与 Backbone 更一致,因为set
会正确更新attributes
,但我再次不确定 Backbone 默认情况下对嵌套模型做了什么。 您可能需要一些自定义代码来处理此问题。
如果你想保持简单,我会选择选项B。
另一个不错的选择是使用骨干关系。您只需定义如下内容:
var Image = Backbone.Model.extend({
relations: [
{
type: Backbone.HasOne,
key: 'layout',
relatedModel: 'Layout'
}
]
});
我使用Backbone DeepModel插件来嵌套模型和属性。
https://github.com/powmedia/backbone-deep-model
您可以绑定到深度更改事件 n 级。 例如: model.on('change:example.nestedmodel.attribute', this.myFunction);
CoffeeScript 版本的 rycfung 的美丽答案:
class ImageModel extends Backbone.Model
model: {
layout: LayoutModel
}
parse: (response) =>
for propName,propModel of @model
response[propName] = new propModel( response[propName], {parse:true, parentModel:this} )
return response
没那么甜吗?;)
我遇到了同样的问题,我一直在试验rycfung答案中的代码,这是一个很好的建议.
但是,如果您不想直接set
嵌套模型,或者不想经常在options
中传递{parse: true}
,另一种方法是重新定义set
本身。
在 Backbone 1.0.0 中,set
在 constructor
、unset
、clear
、fetch
和 save
中被调用。
对于需要嵌套模型和/或集合的所有模型,请考虑以下超级模型。
/** Compound supermodel */
var CompoundModel = Backbone.Model.extend({
/** Override with: key = attribute, value = Model / Collection */
model: {},
/** Override default setter, to create nested models. */
set: function(key, val, options) {
var attrs, prev;
if (key == null) { return this; }
// Handle both `"key", value` and `{key: value}` -style arguments.
if (typeof key === 'object') {
attrs = key;
options = val;
} else {
(attrs = {})[key] = val;
}
// Run validation.
if (options) { options.validate = true; }
else { options = { validate: true }; }
// For each `set` attribute, apply the respective nested model.
if (!options.unset) {
for (key in attrs) {
if (key in this.model) {
if (!(attrs[key] instanceof this.model[key])) {
attrs[key] = new this.model[key](attrs[key]);
}
}
}
}
Backbone.Model.prototype.set.call(this, attrs, options);
if (!(attrs = this.changedAttributes())) { return this; }
// Bind new nested models and unbind previous nested models.
for (key in attrs) {
if (key in this.model) {
if (prev = this.previous(key)) {
this._unsetModel(key, prev);
}
if (!options.unset) {
this._setModel(key, attrs[key]);
}
}
}
return this;
},
/** Callback for `set` nested models.
* Receives:
* (String) key: the key on which the model is `set`.
* (Object) model: the `set` nested model.
*/
_setModel: function (key, model) {},
/** Callback for `unset` nested models.
* Receives:
* (String) key: the key on which the model is `unset`.
* (Object) model: the `unset` nested model.
*/
_unsetModel: function (key, model) {}
});
请注意,model
、_setModel
和_unsetModel
是故意留空的。在此抽象级别,您可能无法为回调定义任何合理的操作。但是,您可能希望在扩展CompoundModel
.
的子模型中覆盖它们例如,这些回调可用于绑定侦听器和传播change
事件。
例:
var Layout = Backbone.Model.extend({});
var Image = CompoundModel.extend({
defaults: function () {
return {
name: "example",
layout: { x: 0, y: 0 }
};
},
/** We need to override this, to define the nested model. */
model: { layout: Layout },
initialize: function () {
_.bindAll(this, "_propagateChange");
},
/** Callback to propagate "change" events. */
_propagateChange: function () {
this.trigger("change:layout", this, this.get("layout"), null);
this.trigger("change", this, null);
},
/** We override this callback to bind the listener.
* This is called when a Layout is set.
*/
_setModel: function (key, model) {
if (key !== "layout") { return false; }
this.listenTo(model, "change", this._propagateChange);
},
/** We override this callback to unbind the listener.
* This is called when a Layout is unset, or overwritten.
*/
_unsetModel: function (key, model) {
if (key !== "layout") { return false; }
this.stopListening();
}
});
这样,您就可以自动创建嵌套模型和传播事件。还提供和测试示例用法:
function logStringified (obj) {
console.log(JSON.stringify(obj));
}
// Create an image with the default attributes.
// Note that a Layout model is created too,
// since we have a default value for "layout".
var img = new Image();
logStringified(img);
// Log the image everytime a "change" is fired.
img.on("change", logStringified);
// Creates the nested model with the given attributes.
img.set("layout", { x: 100, y: 100 });
// Writing on the layout propagates "change" to the image.
// This makes the image also fire a "change", because of `_propagateChange`.
img.get("layout").set("x", 50);
// You may also set model instances yourself.
img.set("layout", new Layout({ x: 100, y: 100 }));
输出:
{"name":"example","layout":{"x":0,"y":0}}
{"name":"example","layout":{"x":100,"y":100}}
{"name":"example","layout":{"x":50,"y":100}}
{"name":"example","layout":{"x":100,"y":100}}
我意识到我参加这个聚会迟到了,但我们最近发布了一个插件来处理这种情况。它被称为骨干嵌套。
因此,嵌套模型保持不变:
var Layout = Backbone.Model.extend({...});
然后在定义包含模型时使用插件(使用 Underscore.extend(:
var spec = {
layout: Layout
};
var Image = Backbone.Model.extend(_.extend({
// ...
}, nestify(spec));
之后,假设你有一个模型m
它是Image
的实例,并且你已经从m
的问题中设置了JSON,你可以这样做:
m.get("layout"); //returns the nested instance of Layout
m.get("layout|x"); //returns 100
m.set("layout|x", 50);
m.get("layout|x"); //returns 50
使用骨干形式
它支持嵌套表单,模型和toJSON。全部嵌套
var Address = Backbone.Model.extend({
schema: {
street: 'Text'
},
defaults: {
street: "Arteaga"
}
});
var User = Backbone.Model.extend({
schema: {
title: { type: 'Select', options: ['Mr', 'Mrs', 'Ms'] },
name: 'Text',
email: { validators: ['required', 'email'] },
birthday: 'Date',
password: 'Password',
address: { type: 'NestedModel', model: Address },
notes: { type: 'List', itemType: 'Text' }
},
constructor: function(){
Backbone.Model.apply(this, arguments);
},
defaults: {
email: "x@x.com"
}
});
var user = new User();
user.set({address: {street: "my other street"}});
console.log(user.toJSON()["address"]["street"])
//=> my other street
var form = new Backbone.Form({
model: user
}).render();
$('body').append(form.el);
如果您不想添加另一个框架,您可以考虑创建一个具有重写set
的基类,并toJSON
并像这样使用它:
// Declaration
window.app.viewer.Model.GallerySection = window.app.Model.BaseModel.extend({
nestedTypes: {
background: window.app.viewer.Model.Image,
images: window.app.viewer.Collection.MediaCollection
}
});
// Usage
var gallery = new window.app.viewer.Model.GallerySection({
background: { url: 'http://example.com/example.jpg' },
images: [
{ url: 'http://example.com/1.jpg' },
{ url: 'http://example.com/2.jpg' },
{ url: 'http://example.com/3.jpg' }
],
title: 'Wow'
}); // (fetch will work equally well)
console.log(gallery.get('background')); // window.app.viewer.Model.Image
console.log(gallery.get('images')); // window.app.viewer.Collection.MediaCollection
console.log(gallery.get('title')); // plain string
您需要从此答案中获得BaseModel
(如果您喜欢,可以作为要点(。
我们也有这个问题,一个团队工作人员已经实现了一个名为 backbone-nested-attributes 的插件。
用法非常简单。例:
var Tree = Backbone.Model.extend({
relations: [
{
key: 'fruits',
relatedModel: function () { return Fruit }
}
]
})
var Fruit = Backbone.Model.extend({
})
有了这个,树模型可以访问果实:
tree.get('fruits')
您可以在此处查看更多信息:
https://github.com/dtmtec/backbone-nested-attributes
- 获取最接近的数组数
- 从同级字符串中指定最接近的元素类
- 接近折线时检测鼠标轨迹
- 当使用'最接近的':jQuery
- 如何使刻度四舍五入到最接近的 # 的倍数
- 如何解析文本区域中的结构化字符串数据(接近JSON)以检索其所需的属性
- 如何根据数值通过数据属性进行搜索并获得最接近的值
- 通过搜索最接近的类来更改类
- 表单验证在接近尾声时停止工作
- 获取TR之外最接近的隐藏值
- JQuery获取最接近的元素并筛选子元素
- 获取最接近所选选项的最快方式
- j查询树遍历从子级到最接近过滤的父级
- JavaScript:如何在最接近值的关联数组中查找键
- jquery onclick 切换最接近的 UL
- 在 javascript 数组中搜索最接近的下一个值
- 如何确定乘以后将使您最接近目标数字的数字
- 单击按钮,如何获取与类最接近的td值
- javascript代码查找最接近零的tescase
- 未获得最接近0的结果