Backbone.Model

来源:互联网 发布:云朵课堂知乎 编辑:程序博客网 时间:2024/06/05 17:14

Backbone.Model概论

一个Backbone 模型可以比作一张表单结构,头部类似表单的列,数据类似表单的行。Backbone.Model对象定义列标签,再用预定义和自定义的方法包裹每一行里的 数据(即属性),以便进行数据转换、验证和访问控制。由Backbone.Model创建模型实例或者一个继承的Backbone.Model,提供对象 到实际数据的存储。


你可以把一个Backbone.Model构造函数比作表单的列标题,其方法和属性通用于每一行的数据。由构造函数创建的一个实例,用实际数据填充上面的表格。

  1. //i.e.
  2. new Backbone.Model(
  3. {firstName:"John",lastName:"Doe",phone:"1-111-1111"}
  4. );

请注意,Backbone.Model为每一行数据提供属性和方法(例如:get("phone")),也支持定义你自己的方法和属性(例如:getFullName())。

以上提到的内容只是Backbone模型的部分特性。在Backbone里,还有一部分内容涉及在HTTP上通过restful JSON api使用AJAX来同步数据。在本文中,我们将不讨论关于同步的细节。本小节主要讨论的是一个模型(以及模型的集合)的生命周期,但不包括同步数据这一 块。

子类与创建一个Backbone.Model

要创建一个通用数据模型,我们只需要从Backbone.Model实例化一个实例,再传入模型将储存的值(也就是Backbone属性)。例如,下面我创建了两个联系模型,一个是JohnDoe,另一个是JaneDoe。

  1. var contact1Model = new Backbone.Model(
  2. {firstName:"John",lastName:"Doe",phone:"111-111-1111"}
  3. );
  4.  
  5. var contact2Model = new Backbone.Model(
  6. {firstName:"Jane",lastName:"Doe",phone:"222-222-2222"}
  7. );

然而,更倾向于先继承/分类基础Backbone.Model,再实例化一个实例,这样你就可以添加自己域特定的属性。

在下面代码的中,我继承了Backbone.Model构造函数,创建出一个子构造函数(ContactModel),并定义getFullName()方法,于是所有创建于ContactModel的模型都将继承getFullName()方法。

  1. var ContactModel = Backbone.Model.extend({ //extend Backbone.Model
  2. getFullName:function(){
  3. return this.get("firstName") +" "+ this.get("lastName");
  4. }
  5. });
  6.  
  7. var contact1Model = new ContactModel( //instantiate ContactModel instance
  8. {firstName:"John",lastName:"Doe",phone:"111-111-1111"}
  9. );
  10.  
  11. var contact2Model = new ContactModel( //instantiate ContactModel instance
  12. {firstName:"Jane",lastName:"Doe",phone:"222-222-2222"}
  13. );
  14.  
  15. //在我们的模型上调用getFullName方法
  16. console.log(contact1Model.getFullName()); //logs John Doe
  17. console.log(contact2Model.getFullName()); //logs Jane Doe

提示:

  1. 当实例化一个模型时,一组数据可以被传入到一个对象的位置。在这之后Backbone将转移这个数组到一个对象,通过给予传递值(["foo","bar"])数值的属性名称,成为了({"1":"foo","2","bar"})。
  2. 实例化模型传递的第二个参数是一个选项对象,包含了collection, url, urlRootparse选项。其中大部分将在下一篇文章中加以详述,因为它们涉及到同步数据。
  3. 别忘了initialize函数,在继承模型时设置的,用于放置创建模型实例需要运行的逻辑关系。尤其需要注意的是,初始化函数中的this值是所创建的模型实例的作用域。
  4. 一种方便的clone()方法在模型实例上可用,将返回一个新的模型实例附带一份clone()方法调用的数据副本。这是从模型实例上创建模型的一种便捷方式。

Backbone.Model方法、属性与事件

Backbone模型实例包含以下方法和属性:

  • get
  • set
  • escape
  • has
  • unset
  • clear
  • id
  • idAttribute
  • cid
  • attributes
  • changed
  • defaults
  • toJSON
  • sync
  • fetch
  • save
  • destroy
  • Underscore Methods:(keys、values、pairs、invert、pick、omit)
  • validate
  • validationError
  • isValid
  • url
  • urlRoot
  • parse
  • clone
  • isNew
  • hasChanged
  • changedAttributes
  • previous
  • previousAttributes

粗体所显示的方法或属性不会在本文章中叙述,在以后的文章中我们会讨论到这些与同步相关的方法或属性。

此外,模型还可以使用以下嵌入事件:

设定模型的默认值/属性

在继承一个模型时,你可以设定一系列默认值共享给所有实例。在下面的代码中,我设置了一个默认值给firstNamelastName

  1. var ContactModel = Backbone.Model.extend({ //继承Backbone.Model
  2. defaults: {
  3. firstName : "no first name yet",
  4. lastName : "no last name yet"
  5. }
  6. });
  7.  
  8. var contact1Model = new ContactModel( //实例化模型实例
  9. {firstName:"John"}
  10. );
  11. // firstName设置为John,同时保持lastname为默认值不变
  12. console.log(contact1Model.get("firstName")); //日志 John
  13. console.log(contact1Model.get("lastName")); //日志"no last name yet"

如果你想让实例不引用同一个默认对象,你可以提供一个默认函数值,将会创造出特殊的默认值(不引用同一个默认对象)。

  1. var ContactModel = Backbone.Model.extend({ //继承Backbone.Model
  2. defaults: function(){
  3. this.firstName = "no first name yet";
  4. this.lastName = "no last name yet";
  5. return this;
  6. }
  7. });
  8.  
  9. var contact1Model = new ContactModel( //实例化模型实例
  10. {firstName:"John"}
  11. );
  12. // firstName设置为John,同时保持lastname默认值不变
  13. console.log(contact1Model.get("firstName")); //日志 John
  14. console.log(contact1Model.get("lastName")); //日志 "no last name yet"

在上面代码中,每一个实例各将会使用一个特殊默认对象,而不是每一个实例引用同一个默认对象。

模型数据的设置、更改、获取、复原与删除

Backbone提供一组通用的方法(set(), has(), get(), unset(), clear())来处理模型数据。下面我将演示每一种方法。

  1. var contact = new Backbone.Model(); //实例化模型实例
  2. //设置/编辑数据
  3. contact.set({firstName:"John",lastname:"Doe",phone:"1-111-1111"});
  4. //拥有firstName属性,注意,无论属性值为何都将返回true
  5. console.log(contact.has("firstName")); //日志 true
  6. //获取数据
  7. console.log(contact.get("firstName")); //日志 John
  8. //复原数据
  9. contact.unset("lastname");
  10. //验证数据已被复原
  11. console.log(contact.has("lastName")); //日志 false
  12. //清除所有数据
  13. contact.clear();
  14. //验证数据已被清除
  15. console.log(contact.attributes); //日志 empty {}

提示:

  1. 这些方法应当被用来替代直接编辑内部attributes对象,以便模型中的事件能够被正确的触发。
  2. set(), unset()clear()方法能够调用内部change事件。
  3. set()方法接收一个选项对象(例如:set({"name":"value","name":"value"}))或两个字符串参数(例如:set("name","value"))。
  4. set(), unset()clear()方法都接收{silent: true}选项,阻止任意Backbone嵌入事件发生。
  5. 在设置值之前,{validate:true}选项和set()方法能够一起用来验证这些值(例如:set({"foo":"foo"},{validate:true}))。

访问一个模型数据/属性

Backbone提供attributes属性,能够直接访问包含一个模型数据的内部对象。

  1. var contact1Model = new Backbone.Model( //instantiate model instance
  2. {firstName:"John",lastName:"Doe"}
  3. );
  4.  
  5. console.log(contact1Model.attributes); //日志 {firstName="John", lastName="Doe"}

Backbone提供直接访问到这个对象,一般情况下该对象不会被直接操纵。事实上,也不要这样做。如果你想编辑模型数据,可以使用set(), get(), unset(), clear()或者复制(model.toJSON();attributes对象,编辑副本之后再利用set()更新模型。

提示:

  • 直接操纵attributes属性将不会调用内部模型事件。

利用toJSON()获取模型数据/属性的一个副本

toJSON()模型方法返回attributes内部数据对象的一个副本。

  1. var contact1Model = new Backbone.Model( //实例化模型实例
  2. {firstName:"John",lastName:"Doe"}
  3. );
  4.  
  5. console.log(contact1Model.toJSON()); //日志 {firstName="John", lastName="Doe"}
  6.  
  7. //请注意,这不是一个引用,而是属性对象的一个副本
  8. //i.e.
  9. console.log(contact1Model.toJSON === contact1Model.attributes); //日志 false

当你需要获取一个对象的干净的副本作为一个模板时,这种方法非常方便。

提示:

  1. 使用toJSON()方法可以获取属性对象的一个浅表克隆( _.clone),任意嵌套对象或数组将从原型引用,而不是复制。 
  2. 传递一个模型实例到JSON.stringify,将内部使用Backbone的toJSON()方法。

过滤模型数据

Backbone从underscore.js库代理6种方法(keys(), values(), pairs(), invert(), pick(), omit()),都可以直接用在模型数据上。这些函数可以为过滤所需模型数据状态提供帮助。下面我将展示最常用的4种方法。

  1. var contact = new Backbone.Model({
  2. firstName:"John",lastName:"Doe",phone:"1-111-1111"
  3. });
  4.  
  5. //返回数据的数组/属性键
  6. console.log(contact.keys()); //日志 ["firstName", "lastName", "phone"]
  7.  
  8. //返回数据的数组/属性值
  9. console.log(contact.values()); //日志 ["John", "Doe", "1-111-1111"]
  10.  
  11. //复制数据/属性对象,返回包含选中键的对象
  12. console.log(contact.pick("phone")); //日志 {phone="1-111-1111"}
  13.  
  14. //复制数据/属性对象,返回不包含选中键的对象
  15. console.log(contact.omit("firstName","lastName")); // 日志 {phone="1-111-1111"}

监听模型change事件

当模型中的数据发生变化时,change事件会同时被广播,通常情况下一个视图能够监听此类事件。

在下面的代码中,模型创建之后,又创建了一个视图来监听模型上的change事件。模型数据的任何改变都将触发一个change事件,理想情况下,还会有一个重新渲染。

  1. var contact= new Backbone.Model();
  2.  
  3. var ContactView = Backbone.View.extend({
  4. initialize:function(){
  5. this.listenTo(contact,"change",this.render); //listen to change event on model
  6. },
  7. render:function(){
  8. console.log("data changed, re-render");
  9. }
  10. });
  11.  
  12. new ContactView();
  13.  
  14. contact.set({ //这将触发change事件
  15. firstName:"john",lastName:"Doe",phone:"111-111-1111"
  16. });
  17.  
  18. contact.unset("phone"); //这将触发change事件

还可以只监听模型上一个特殊属性的变化,需要将属性名称和change事件指定给一个监听者("change:[ATTRIBUTE]")。比如,下面我将在原来的代码上加以变化,只对联系模型上的firstName属性改变加以监听。

  1. var contact= new Backbone.Model();
  2.  
  3. var ContactView = Backbone.View.extend({
  4. initialize:function(){
  5. //监听模型上的change事件
  6. this.listenTo(contact,"change:firstName",this.render);
  7. },
  8. render:function(){console.log("render a change, data changed");}
  9. });
  10.  
  11. new ContactView();
  12. //change事件被激活,因为我们只监听firstName
  13. contact.set("firstName","john");
  14. contact.set("phone","222-222-2222"); //no change event trigger

利用hasChanged()和changedAttributes()验证一个模型已变化与变化的内容

hasChanged()changedAttributes()模型方法能够验证在最后一个change事件之后变化是否已生效,并且验证具体是什么发生了变化。

在下面代码中,我设置了一个联系模型,然后立即改变电话号码属性。

  1. //创建联系模型,用数据实例化模型时并没有change事件被激活
  2. var contact=new Backbone.Model({firstName:"John",lastName:"Doe",phone:"1-111-1111"});
  3.  
  4. //做出一个改变
  5. contact.set({phone:"2-222-2222"});
  6.  
  7. //验证一个变化已经发生
  8. console.log(contact.hasChanged()); //logs true
  9.  
  10. //获取已改变的属性
  11. console.log(contact.changedAttributes()); //logs {phone="2-222-2222"}
  12. console.log(contact.changed); //obj{phone="2-222-2222"}
  13. console.log(contact.changed.phone); //"2-222-2222"

代码中,通过改变电话号码,内部change事件被立即调用了,hasChanged()方法返回一个布尔函数表示模型已经发生了改变。为了得到包含该属性的发生改变的一个对象,需要在模型上调用changedAttibutes()方法。

利用previous()与previousAttributes()获取原始属性值

previous()previousAttributes()方法能够用来获取change事件之前的一个属性值。在下面的代码中,我设置了一个联系模型,更改了phonefirstName属性,然后利用previous()previousAttributes()方法检索号码属性的前值,以及最后change事件之前的所有模型属性状态。

  1. //创建联系模型,用数据实例化模型时没有change事件被激活
  2. var contact=new Backbone.Model({firstName:"John",lastName:"Doe",phone:"111-111-1111"});
  3.  
  4. //做出一个改变
  5. contact.set({phone:"2-222-2222",firstName:"Jane"});
  6.  
  7. //获取电话号码的初始值
  8. console.log(contact.previous("phone")); //日志 111-111-1111
  9.  
  10. //获取最后change事件之前的所有属性状态
  11. console.log(contact.previousAttributes()); //日志 {firstName:"John",lastName:"Doe",phone:"1

在模型设置或存储之前验证模型数据

当设置或存储模型数据(在下一篇文章中将讨论存储)之前,一个验证函数将被调用来验证数据质量。

默认情况下,validate属性还未定义。在模型上为了定义一个验证函数,可以传递validate:function(attributes,options){}extend(),或者直接更新模型实例上的validate属性。一个验证错误跳闸,就是简单的从一个接收false的函数返回任意值。

在下面的代码中,我为电话号码设置了一个验证函数,确保任意无效的电话号码将不会被set()

  1. var contact = new Backbone.Model({
  2. firstName:"John",lastName:"Doe",phone:0
  3. });
  4.  
  5. contact.validate = function(attributes,options){
  6. var validPhone = /\(?\d{3}\W?\s?\d{3}\W?\d{4}/.test(attributes.phone);
  7. if(!validPhone){
  8. 返回"Setting or Saving Invalid Phone Number Attempted";
  9. }
  10. }
  11.  
  12. contact.set("phone","111-111-1111",{validate:true}); //set
  13. contact.set("phone","111-1-1111",{validate:true}); //不会set,验证失败
  14.  
  15. console.log(contact.get("phone")); //没有无效的电话号码
  16. console.log(contact.validationError); //读取最后一个验证错误

从验证函数最后一个返回的值如果不是false,则可以引用模型validationError属性。代码中我们从验证函数返回一个无效的信息,并访问validationError属性,后台输出这个信息。

提示:

  1. 一个validate回调函数将所有模型属性作为第一个参数传递,第二个参数包含set()save()选项对象可以验证跳闸。
  2. 验证只发生在使用save()set()方法,与validation:true选项,或者利用isValid()进行手动验证。

监听一个模型的invalid事件

当一个模型的validate函数失败,嵌入invalid事件将被广播。在下面的代码中,我演示了监听这个事件并输出参数传递给无效的回调函数。

  1. var contact = new Backbone.Model({firstName:"John",lastName:"Doe"});
  2.  
  3. contact.validate = function(attributes,options){
  4. var validPhone = /\(?\d{3}\W?\s?\d{3}\W?\d{4}/.test(attributes.phone);
  5. if(!validPhone){返回"Setting or Saving Invalid Phone Number Attempted";}
  6. };
  7.  
  8. //Backbone在联系模型上监听无效事件
  9. Backbone.listenTo(contact,"invalid",function(model,error,options){
  10. console.log(model,error,options);
  11. });
  12.  
  13. //设置无效电话号码触发无效事件
  14. contact.set("phone","111-1-1111",{validate:true}); //不会set,验证失败

在模型上手动运行validate 

validate函数能够通过调用isValid()方法被手动调用。在下面的代码中,validate函数在继承Backbone.model构造函数时被设置,因此所有模型实例都能使用这个验证函数。在创建模型实例过程中,为了保证数据都被验证了,我在调用initialize时将运行validate函数,来获取通过了的无效数据。

  1. var ContactModel = Backbone.Model.extend({ //extend Backbone.Model
  2. validate :function(attributes,options){
  3. var phone = /\(?\d{3}\W?\s?\d{3}\W?\d{4}/.test(attributes.phone);
  4. var firstName = /^[A-Z]"?[a-zA-Z]+(-[a-zA-Z]+)?$/.test(attributes.firstName);
  5. var lastName = /^[A-Z]"?[a-zA-Z]+(-[a-zA-Z]+)?$/.test(attributes.lastName);
  6. if(!phone !firstName !lastName){
  7. return "Setting, Saving, Or Seeding Invalid Data"
  8. }
  9. },
  10. initialize:function(){
  11. //运行验证, 如果无效将输出信息
  12. if(!this.isValid()){console.log(this.validationError)}
  13. }
  14. });
  15. var contact1Model = new ContactModel({ //validate seeded data
  16. firstName:"jo234 hn",lastName:"ao321-- e",phone:"111-1111-111111"
  17. });

请注意,isValid()方法返回一个有用的布尔函数,显示模式是否有效。


0 0
原创粉丝点击