サーバーから次のJSONが提供されています。これで、ネストされたモデルでモデルを作成したいと思います。どちらがこれを達成する方法なのかはわかりません。
//json
[{
name : "example",
layout : {
x : 100,
y : 100,
}
}]
これらを、次の構造を持つ2つのネストされたバックボーンモデルに変換する必要があります。
// structure
Image
Layout
...
そのため、次のようにレイアウトモデルを定義します。
var Layout = Backbone.Model.extend({});
しかし、イメージモデルを定義するために、以下の2つの(存在する場合)テクニックのどちらを使用する必要がありますか?以下のAまたはB?
[〜#〜] a [〜#〜]
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;
}
});
モデル自体を改ざんしたわけではなく、単に解析メソッドから目的のオブジェクトを返すだけです。
これにより、サーバーから読み取るときに、ネストされたモデルの構造が確保されます。さて、適切なモデルを使用してネストされたモデルを明示的に設定するのが理にかなっていると思うので、保存または設定は実際にはここでは処理されないことに気付くでしょう。
そのようです:
image.set({layout : new Layout({x: 100, y: 100})})
また、次の呼び出しにより、ネストされたモデルで実際に解析メソッドを呼び出していることに注意してください。
new embeddedClass(embeddedData, {parse:true});
model
フィールドには、必要な数のネストされたモデルを定義できます。
もちろん、ネストされたモデルを独自のテーブルに保存する場合に限ります。これでは十分ではありません。ただし、オブジェクト全体を読み取って保存する場合は、このソリューションで十分です。
解析を再定義するためのPeter Lyonの提案の例として、このコードを投稿しています。私は同じ質問を持っていて、これは私のために働いた(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
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
from バックボーンアソシエーション :
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自体にこれを行うための推奨される方法があるかどうかはわかりません。 Layoutオブジェクトには独自のIDがあり、バックエンドデータベースに記録されていますか?その場合は、お持ちのモデルを独自のモデルにすることができます。そうでない場合は、ネストされたドキュメントのままにして、save
メソッドとparse
メソッドでJSONとの間で適切に変換するようにしてください。結局このようなアプローチをとるなら、あなたの[〜#〜] a [〜#〜]の例は、set
がattributes
ですが、ここでもデフォルトでBackboneがネストされたモデルで何をするのかわかりません。これを処理するには、カスタムコードが必要になる可能性があります。
物事をシンプルに保ちたい場合は、オプションBを使用します。
別の良いオプションは、 Backbone-Relational を使用することです。次のように定義するだけです。
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);
rycfung's美しい答えのCoffeeScriptバージョン:
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
したくない場合、または{parse: true}
でoptions
を使用する場合、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}}
私はこのパーティーに遅れていることに気付きましたが、最近、まさにこのシナリオに対処するプラグインをリリースしました。 backbone-nestify と呼ばれます。
したがって、ネストされたモデルは変更されません。
var Layout = Backbone.Model.extend({...});
次に、包含モデルを定義するときにプラグインを使用します( nderscore.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: "[email protected]"
}
});
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')
詳細については、こちらをご覧ください。