BackBone 教程 - 第三部分:更多关于 BackBone 模型





5.00/5 (19投票s)
与 BackBone 模型相关的更多概念
引言
在本文中,我们将探讨更多与 Backbone 模型相关的概念。我们将尝试了解如何覆盖默认的模型行为。我们将研究模型 ID 的重要性,如何验证模型,最后,模型如何本地或在服务器上持久化。
背景
在前面的几篇文章中,我们已经讨论了 Backbone 和 Backbone 模型的基础知识。本文是该系列文章的延续。
完整系列链接
- BackBone 教程 – 第一部分:Backbone.Js 简介[^]
- BackBone 教程 – 第二部分:理解 Backbone 模型基础[^]
- BackBone 教程 – 第三部分:更多关于 Backbone 模型[^]
- BackBone 教程 – 第四部分:使用 HTTP REST 服务在 Backbone.Js 模型上进行 CRUD 操作[^]
- BackBone 教程 – 第五部分:理解 Backbone.Js 集合[^]
- BackBone 教程 – 第六部分:理解 Backbone.Js 视图[^]
- BackBone 教程 – 第七部分:理解 Backbone.Js 路由和历史[^]
- BackBone 教程 – 第八部分:理解 Backbone.Js 事件[^]
Using the Code
initialize 函数和构造函数
每当我们创建一个模型时,Backbone 都会调用其 initialize
函数。我们可以覆盖此函数为其提供自定义行为。
var Book = Backbone.Model.extend({
defaults: {
ID: "",
BookName: ""
},
initialize: function () {
console.log('Book has been initialized');
},
});
因此,当我们创建此对象时,输出将是
内部发生的情况是,每当创建一个 Backbone 模型时,都会调用其构造函数。构造函数将调用 initialize
函数。我们也可以提供自己的构造函数并提供自定义行为。
var Book = Backbone.Model.extend({
defaults: {
ID: "",
BookName: ""
},
initialize: function () {
console.log('Book has been initialized');
},
constructor: function (attributes, options) {
console.log('Book\'s constructor had been called');
},
});
创建此模型时的输出
现在,这个构造函数的问题在于,每当创建 Backbone 模型时,都会调用我们的构造函数。但是默认构造函数在对象构造时还会执行许多其他活动,例如调用 initialize
函数。因此,为了确保我们的自定义构造函数能够与所有默认行为协同工作,我们需要让 Backbone 框架知道我们仍然想要默认行为。这可以通过在自定义构造函数末尾调用 Backbone.Model.apply(this, arguments);
来完成。这将确保我们的自定义构造函数被调用,然后默认构造函数应执行的所有其他活动也一并完成。
var Book = Backbone.Model.extend({
defaults: {
ID: "",
BookName: ""
},
initialize: function () {
console.log('Book has been initialized');
},
constructor: function (attributes, options) {
console.log('Book\'s constructor had been called');
Backbone.Model.apply(this, arguments);
},
});
现在输出将是
注意:在大多数实际用途中,覆盖 initialize
函数就足够了。很少需要覆盖构造函数,但如果决定覆盖构造函数,则应按此方式进行。
模型标识符 - id, cid 和 idAttribute
每个模型都需要被唯一标识。为此,Backbone 为我们提供了模型标识符。第一个要看的是 cid
。cid
或客户端 ID 是由 Backbone 自动生成的,以便每个模型都可以在客户端唯一标识。
var book1 = new Book();
var book2 = new Book();
Backbone 还提供了一个标识符 id
来唯一标识模型实体。这是模型数据实际与服务器同步(即持久化)时用于标识模型的 id。cid
对于调试目的更有用,但 id
属性将决定模型在对模型进行 CRUD 操作时的唯一性。设置和获取 id
属性非常直接。
var book2 = new Book();
book2.id = 3;
console.log(book2.id);
上述代码的输出为:3
。
此时会有点令人困惑。由于我们的大多数模型都有一个对应于实体主键/唯一标识符的属性,我们是否需要显式地将 id
值设置为该属性。答案是肯定的,也否定的。我们必须以某种方式向 Backbone 模型指示哪个属性应该用作 id
,但不必显式设置 id
。我们可以使用 idAttribute
来实现这一点。
var Book = Backbone.Model.extend({
defaults: {
ID: "",
BookName: ""
},
idAttribute: "ID",
initialize: function () {
console.log('Book has been initialized');
},
constructor: function (attributes, options) {
console.log('Book\'s constructor had been called');
Backbone.Model.apply(this, arguments);
},
});
在上面的代码中,我们通过指定 idAttribute
来指定 ID 应该用作 id
。现在,让我们尝试使用 ID
值创建一个新模型。
var book3 = new Book({ ID: 43 });
console.log(book1.id);
我们可以看到 id
值是从指定的属性中获取的。
因此,这使得 Backbone 模型与服务器端实体协同工作变得更加容易,并使模型标识无缝。
验证模型
当我们处理业务应用程序时,通常需要在持久化数据之前验证模型。Backbone 提供了一种非常简单的方法来验证模型数据。我们只需要实现模型的 validate
函数。
var Book = Backbone.Model.extend({
defaults: {
ID: "",
BookName: ""
},
idAttribute: "ID",
initialize: function () {
console.log('Book has been initialized');
},
constructor: function (attributes, options) {
console.log('Book\'s constructor had been called');
Backbone.Model.apply(this, arguments);
},
validate: function (attr) {
if (attr.ID <= 0) {
return "Invalid value for ID supplied."
}
}
});
这里发生的是,每当我们尝试保存模型(我们将在下一节中看到)时,都会调用 Validate
函数。它将检查我们已设置的自定义验证逻辑并验证模型。要测试 validate
方法,我们可以使用模型的 isValid
函数。
var book4 = new Book({ ID: -4 });
var result = book4.isValid(); // false
另一种防止模型属性中出现无效值的方法是在设置模型属性时传递 validate:true
。这也将触发 validate
函数。
var book5 = new Book();
book5.set("ID", -1, {validate:true});
这将不会允许设置无效值,即使该值根据我们的自定义逻辑是无效的。
此验证的工作方式是,每当用户选择保存模型时,都会调用 validate
函数。如果存在任何验证错误,则模型保存将失败。或者,用户可以选择在想要限制模型属性中的无效值设置时传递 validate:true
。如果我们想在任何特定实例上检查模型的有效性,我们可以使用 isValid
函数进行测试。话虽如此,这里要了解的一个重要事情是,每当我们的验证函数未能验证模型时,Backbone 就会引发一个 invalid
事件。如果我们想监听此事件,可以订阅它。让我们尝试挂接到此事件并查看验证错误。我们将在模型的 initialize
函数中执行此操作。
var Book = Backbone.Model.extend({
defaults: {
ID: "",
BookName: ""
},
idAttribute: "ID",
initialize: function () {
console.log('Book has been initialized');
this.on("invalid", function (model, error) {
console.log("Houston, we have a problem: " + error)
});
},
constructor: function (attributes, options) {
console.log('Book\'s constructor had been called');
Backbone.Model.apply(this, arguments);
},
validate: function (attr) {
if (attr.ID <= 0) {
return "Invalid value for ID supplied."
}
}
});
保存模型
Backbone 模型天然支持使用 RESTful Web API 在服务器上保存。要使用 HTTP REST 服务保存模型,我们需要在 Backbone 模型中指定 urlRoot
。要实际保存模型,我们可以调用 Backbone 模型上的 save
。save
方法将触发验证,如果验证成功,它将尝试识别要执行的操作,即创建或更新,并且基于该操作,它将使用 urlRoot
并调用适当的 REST API 来执行操作。
所以,如果我在本地机器上运行一个服务,我首先需要在我的模型中为该服务指定 urlRoot
。
var Book = Backbone.Model.extend({
defaults: {
ID: "",
BookName: ""
},
idAttribute: "ID",
initialize: function () {
console.log('Book has been initialized');
this.on("invalid", function (model, error) {
console.log("Houston, we have a problem: " + error)
});
},
constructor: function (attributes, options) {
console.log('Book\'s constructor had been called');
Backbone.Model.apply(this, arguments);
},
validate: function (attr) {
if (attr.ID <= 0) {
return "Invalid value for ID supplied."
}
},
urlRoot: 'https://:51377/api/Books'
});
要使用此服务保存此模型,我可以这样做:
var book = new Book({ BookName: "Backbone Book 43" });
book.save({}, {
success: function (model, response, options) {
console.log("The model has been saved to the server");
},
error: function (model, xhr, options) {
console.log("Something went wrong while saving the model");
}
});
save
函数还接受 success 和 error 回调函数,以便可以根据服务器的响应采取适当的操作。
现在,如果我们想将模型保存在本地存储而不是服务器上,我们只需要记住 save 函数实际上调用 sync 函数来实际保存/检索模型信息。因此,如果我们想将模型保存在本地存储中,我们需要覆盖 sync 函数并提供自定义代码以保存在本地存储中。
注意:上面显示的(用于 save
模型的)代码在语法上是正确的,但除非我们在指定的 urlRoot
处运行 REST 服务,否则它将不起作用。在接下来的文章中,我将详细介绍使用 REST 服务保存模型的代码,并附带示例服务和 HTML 代码。
看点
因此,我们看到了有关 Backbone 模型的一些更多细节。我们还没有研究如何将模型本地保存或通过服务保存。也许在我的下一篇文章中,我们将只讨论这个问题。
历史
- 2014年7月16日:第一个版本