BackboneJs - 在模型更改时保留模型内集合的事件
BackboneJs - Retain events on a collection inside a model when model changes
我在模型中有一个集合,如下图所示:
var itemModel = Backbone.Model.extend({
defaults:{
name:"",
brand:"",
priceCollection:[]
}
})
有附加到 itemModel
的更改侦听器以及附加到集合的更改侦听器作为
this.listenTo(itemModel.get('priceCollection'),'change',this.dosomething)
在视图中。
问题是集合上的更改侦听器可以正常工作只要父模型没有更改,如果模型通过 itemModel.set(newattributes)
itemModel.get('priceCollection') 绑定的事件丢失。
如何保留此活动?还是每次模型更改时我都应该重新绑定此事件?或者我应该将集合上的侦听器从视图移动到模型并触发自定义 Backbone 事件吗?
需要注意的是这个模型是单例的
请记住,Backbone 假定集合和模型应映射 1:1 到服务器端资源。它对 API 布局和数据结构做出了明确的假设 - 请参阅 Model.url
, Model.urlRoot
and Collection.url
.
提案
你说模型是单例。在这种情况下,我建议单独维护模型和集合。
由于 SomeModel
没有伴随着具有紧密关系的特定集合 SomeCollection
,因此没有必要在属性级别上将它们关联起来。建立事件侦听器和同步数据所需的工作仅在一处。
// some controller (app main)
var model = new SomeSingletonModel();
var collection = new SomeSingletonCollection();
var view = new SomeView({
model: model,
collection: collection
});
可能映射到SomeSingletonModel
的资源会传送一个数组。
使用集合作为模型属性(model.get("name")
是什么)比使用普通数组有什么好处?同步和更改事件。两者可能仅在视图更新集合的模型时才需要。当 View 仅呈现时,Collection 在许多情况下不会提供任何好处。
如果需要更新该数组的数据,使用 Collection 可能是正确的选择,因为 Backbone 的同步机制。
但是如何使集合与模型保持同步(你问)?
您的控制器需要监听模型并在 sync
和 reset
上更新集合:
model.on("sync reset", function() {
// "priceCollection" is a model attribute
collection.reset(model.get("priceCollection"));
// optionally unset "priceCollection" on the model
this.unset("priceCollection", { silent: true });
});
这将初始化集合。
对集合模型的任何更改将仅作为集合或模型同步机制的一部分。
前言
另见 my other answer 当模型是单例时,这可能是更好的选择。
请注意关于 Backbone 对 API 设计的假设的第一个陈述。
使用 Model 和 Collection
之间的耦合的提案
注意:如有必要,在所有这些实现中 Collection 的 url
或模型的(Collection 的模型)url
/rootUrl
可能会在 sync
上得到(重新)定义以控制同步。
更新 change/sync/reset
的内部参考
此实现删除模型属性并使用其数据更新 object 属性。
object 属性是一个 Collection 实例,在模型更改时只会重置,不会重新创建。
var CustomModel = Backbone.Model.extend({
defaults: {
// defaults go here - "children" may be contained here
},
// implement constructor to act before the parent constructor is able to
// call set() (L402 in v1.3.0) with the initial values
// See https://github.com/jashkenas/backbone/blob/1.3.0/backbone.js#L402
constructor: function() {
// create children collection as object attribute - replaces model attr.
this.children = new Backbone.Collection();
// listen to changing events to catch away that attribute an update the
// object attribute
this.listenTo(this, "change:children sync reset", this.onChangeColl);
// apply original constructor
Backbone.Model.apply(this, arguments);
},
onChangeColl: function() {
// check for presence since syncing will trigger "set" and then "sync",
// the latter would then empty the collection again after it has been updated
if (this.has("children")) {
// update "children" on syncing/resetting - this will trigger "reset"
this.children.reset(this.get("children"));
// remove implicitly created model attribute
// use silent to prevent endless loop due to change upon change event
this.unset("children", { silent: true });
}
}
});
在 Fiddle 或控制台中测试时的用法示例:
var c = new CustomModel({ a: 1, children: [{ x: 1 }, { x: 5 }] });
c.set({a: 8, children: [{ x: 50 }, { x: 89 }]});
c.url = "/dummy"
// replace sync() only for fetch() demo - the implementation does what sync() would do on success
c.sync = function(method, coll, opts){ if (method == "read") { opts.success({ a: 100, children: [{ x: 42 }, { x: 47 }] }); } }
c.fetch();
临
- 监听 collection 事件更容易实现,因为模型生命周期只有一个实例
魂斗罗
- 代码更复杂
- collection 如果没有进一步实施,数据不会部分同步
替换为 change/sync/reset
此实现拦截模型属性更改并将其数据替换为已使用原始数据初始化(重置)的 Collection 实例。
var CustomModel = Backbone.Model.extend({
defaults: {
// this is optional
children: new Backbone.Collection()
},
initialize: function() {
// listen to model attribute changing events to swap the raw data with a
// collection instance
this.listenTo(this, "change:children sync reset", this.onChangeColl);
},
onChangeColl: function() {
if (this.has("children")) {
// use silent to prevent endless loop due to change upon change event
this.set("children", new Backbone.Collection(this.get("children")), { silent: true });
}
}
});
在 Fiddle 或控制台中测试时的用法示例:
var c = new CustomModel({ a: 1, children: [{ x: 1 }, { x: 5 }] });
c.set({ a: 8, children: [{ x: 50 }, { x: 89 }] });
c.url = "/dummy";
// replace sync() only for fetch() demo - the implementation does what sync() would do on success
c.sync = function(method, coll, opts){ if (method == "read") { opts.success({ a: 100, children: [{ x: 42 }, { x: 47 }] }); } }
c.fetch();
临
- 直接实施
魂斗罗
- 同步中包含的数据,排除需要更多的努力
- 听 Collection 不切实际:因为所有消费者都需要 unbind/bind
注意:根据您的要求和 API 设计,您可能不希望 children
自动同步到服务器。在这种情况下,该解决方案是有限的。您可以覆盖模型的 toJSON()
,但这可能会限制它在应用程序其他部分的使用(例如将数据输入视图)。
反向关系:Collection 有一个模型
也许您的主要数据实际上是Collection。所以用额外的数据装饰 Collection 是另一种方法。此实现提供了一个沿 Collection 的模型,该模型将在 collection 同步时更新。
此实现仅最适合获取 collection 数据和属性(例如,使用目录本身的属性获取目录内容)。
var CustomCollection = Backbone.Collection.extend({
initialize: function() {
// maintain decorative attributes of this collection
this.attrs = new Backbone.Model();
},
parse: function(data, opts) {
// remove "children" before setting the remainder to the Model
this.attrs.set(_.omit(data, "children"));
// return the collection content only
return data.children;
}
});
在 Fiddle 或控制台中测试时的用法示例:
var c = new CustomCollection({ a: 1, b: 2, children: [{ x: 2 }, { x: 3 }] }, { parse: true });
c.reset({ a: 9, b: 11, children: [{ x: 5 }, { x: 10 }] } , { parse: true });
// replace sync() only for fetch() demo - the implementation does what sync() would do on success
c.sync = function(method, coll, opts){ if (method == "read") { opts.success({ a: 100, b: 124, children: [{ x: 42 }, { x: 47 }] }) } }
c.fetch();
临
- 直接实施
- 委托模型事件更容易
魂斗罗
- collection 如果没有进一步实施,数据不会部分同步
- 需要
parse()
来实现
-- 依次要求 parse: true
始终传递给 reset()
和 set()
以及
-- 要求 parse()
以 collection 作为范围调用 (this
)(这可以通过在绑定到 [=27 的 initialize
中定义 parse
来规避=] 使用 `bind()´)
我在模型中有一个集合,如下图所示:
var itemModel = Backbone.Model.extend({
defaults:{
name:"",
brand:"",
priceCollection:[]
}
})
有附加到 itemModel
的更改侦听器以及附加到集合的更改侦听器作为
this.listenTo(itemModel.get('priceCollection'),'change',this.dosomething)
在视图中。
问题是集合上的更改侦听器可以正常工作只要父模型没有更改,如果模型通过 itemModel.set(newattributes)
itemModel.get('priceCollection') 绑定的事件丢失。
如何保留此活动?还是每次模型更改时我都应该重新绑定此事件?或者我应该将集合上的侦听器从视图移动到模型并触发自定义 Backbone 事件吗?
需要注意的是这个模型是单例的
请记住,Backbone 假定集合和模型应映射 1:1 到服务器端资源。它对 API 布局和数据结构做出了明确的假设 - 请参阅 Model.url
, Model.urlRoot
and Collection.url
.
提案
你说模型是单例。在这种情况下,我建议单独维护模型和集合。
由于 SomeModel
没有伴随着具有紧密关系的特定集合 SomeCollection
,因此没有必要在属性级别上将它们关联起来。建立事件侦听器和同步数据所需的工作仅在一处。
// some controller (app main)
var model = new SomeSingletonModel();
var collection = new SomeSingletonCollection();
var view = new SomeView({
model: model,
collection: collection
});
可能映射到SomeSingletonModel
的资源会传送一个数组。
使用集合作为模型属性(model.get("name")
是什么)比使用普通数组有什么好处?同步和更改事件。两者可能仅在视图更新集合的模型时才需要。当 View 仅呈现时,Collection 在许多情况下不会提供任何好处。
如果需要更新该数组的数据,使用 Collection 可能是正确的选择,因为 Backbone 的同步机制。
但是如何使集合与模型保持同步(你问)?
您的控制器需要监听模型并在 sync
和 reset
上更新集合:
model.on("sync reset", function() {
// "priceCollection" is a model attribute
collection.reset(model.get("priceCollection"));
// optionally unset "priceCollection" on the model
this.unset("priceCollection", { silent: true });
});
这将初始化集合。
对集合模型的任何更改将仅作为集合或模型同步机制的一部分。
前言
另见 my other answer 当模型是单例时,这可能是更好的选择。
请注意关于 Backbone 对 API 设计的假设的第一个陈述。
使用 Model 和 Collection
之间的耦合的提案注意:如有必要,在所有这些实现中 Collection 的 url
或模型的(Collection 的模型)url
/rootUrl
可能会在 sync
上得到(重新)定义以控制同步。
更新 change/sync/reset
的内部参考此实现删除模型属性并使用其数据更新 object 属性。
object 属性是一个 Collection 实例,在模型更改时只会重置,不会重新创建。
var CustomModel = Backbone.Model.extend({
defaults: {
// defaults go here - "children" may be contained here
},
// implement constructor to act before the parent constructor is able to
// call set() (L402 in v1.3.0) with the initial values
// See https://github.com/jashkenas/backbone/blob/1.3.0/backbone.js#L402
constructor: function() {
// create children collection as object attribute - replaces model attr.
this.children = new Backbone.Collection();
// listen to changing events to catch away that attribute an update the
// object attribute
this.listenTo(this, "change:children sync reset", this.onChangeColl);
// apply original constructor
Backbone.Model.apply(this, arguments);
},
onChangeColl: function() {
// check for presence since syncing will trigger "set" and then "sync",
// the latter would then empty the collection again after it has been updated
if (this.has("children")) {
// update "children" on syncing/resetting - this will trigger "reset"
this.children.reset(this.get("children"));
// remove implicitly created model attribute
// use silent to prevent endless loop due to change upon change event
this.unset("children", { silent: true });
}
}
});
在 Fiddle 或控制台中测试时的用法示例:
var c = new CustomModel({ a: 1, children: [{ x: 1 }, { x: 5 }] });
c.set({a: 8, children: [{ x: 50 }, { x: 89 }]});
c.url = "/dummy"
// replace sync() only for fetch() demo - the implementation does what sync() would do on success
c.sync = function(method, coll, opts){ if (method == "read") { opts.success({ a: 100, children: [{ x: 42 }, { x: 47 }] }); } }
c.fetch();
临
- 监听 collection 事件更容易实现,因为模型生命周期只有一个实例
- 代码更复杂
- collection 如果没有进一步实施,数据不会部分同步
替换为 change/sync/reset
此实现拦截模型属性更改并将其数据替换为已使用原始数据初始化(重置)的 Collection 实例。
var CustomModel = Backbone.Model.extend({
defaults: {
// this is optional
children: new Backbone.Collection()
},
initialize: function() {
// listen to model attribute changing events to swap the raw data with a
// collection instance
this.listenTo(this, "change:children sync reset", this.onChangeColl);
},
onChangeColl: function() {
if (this.has("children")) {
// use silent to prevent endless loop due to change upon change event
this.set("children", new Backbone.Collection(this.get("children")), { silent: true });
}
}
});
在 Fiddle 或控制台中测试时的用法示例:
var c = new CustomModel({ a: 1, children: [{ x: 1 }, { x: 5 }] });
c.set({ a: 8, children: [{ x: 50 }, { x: 89 }] });
c.url = "/dummy";
// replace sync() only for fetch() demo - the implementation does what sync() would do on success
c.sync = function(method, coll, opts){ if (method == "read") { opts.success({ a: 100, children: [{ x: 42 }, { x: 47 }] }); } }
c.fetch();
临
- 直接实施
- 同步中包含的数据,排除需要更多的努力
- 听 Collection 不切实际:因为所有消费者都需要 unbind/bind
注意:根据您的要求和 API 设计,您可能不希望 children
自动同步到服务器。在这种情况下,该解决方案是有限的。您可以覆盖模型的 toJSON()
,但这可能会限制它在应用程序其他部分的使用(例如将数据输入视图)。
反向关系:Collection 有一个模型
也许您的主要数据实际上是Collection。所以用额外的数据装饰 Collection 是另一种方法。此实现提供了一个沿 Collection 的模型,该模型将在 collection 同步时更新。
此实现仅最适合获取 collection 数据和属性(例如,使用目录本身的属性获取目录内容)。
var CustomCollection = Backbone.Collection.extend({
initialize: function() {
// maintain decorative attributes of this collection
this.attrs = new Backbone.Model();
},
parse: function(data, opts) {
// remove "children" before setting the remainder to the Model
this.attrs.set(_.omit(data, "children"));
// return the collection content only
return data.children;
}
});
在 Fiddle 或控制台中测试时的用法示例:
var c = new CustomCollection({ a: 1, b: 2, children: [{ x: 2 }, { x: 3 }] }, { parse: true });
c.reset({ a: 9, b: 11, children: [{ x: 5 }, { x: 10 }] } , { parse: true });
// replace sync() only for fetch() demo - the implementation does what sync() would do on success
c.sync = function(method, coll, opts){ if (method == "read") { opts.success({ a: 100, b: 124, children: [{ x: 42 }, { x: 47 }] }) } }
c.fetch();
临
- 直接实施
- 委托模型事件更容易
- collection 如果没有进一步实施,数据不会部分同步
- 需要
parse()
来实现 -- 依次要求parse: true
始终传递给reset()
和set()
以及 -- 要求parse()
以 collection 作为范围调用 (this
)(这可以通过在绑定到 [=27 的initialize
中定义parse
来规避=] 使用 `bind()´)