本文紧接第二部分:《BackboneJS框架的技巧及模式(3)》
作者:chszs,转载需注明。博客主页:http://blog.csdn.net/chszs
这一条也经常会对那些Backbone.js新手造成困扰。要想在调用.save()的时候让模型发送一个HTTP PUT请求,你的模型需要有一个ID属性集。记得HTTP PUT谓词是设计用做更新的吧,所以发送一PUT请求,你的模型需要有一个ID。在理想情况,你的所有模型都有一名为ID的完美ID属性,但是你从端点接收到的JSON数据或许并不总是有完美命名的ID。
因此,如果你需要更新一个模型,在保存前确保模型上带有ID。Backbone.js从0.5版开始允许使用idAttribute来修改模型ID属性的名称,如果端点返回的不是名为id的ID属性名时。
如果使用的Backbone.js版本低于0.5,建议你修改模型或集合的parse函数,以便将期望的ID属性映射到属性ID。这里有一个快速上手的例子,说明了应怎样修改parse函数来做到这一点。我们假设你有一个cars的集合,它的ID属性名是carID。
parse: function(response) { _.each(response.cars, function(car, i) { // map the returned ID of carID to the correct attribute ID response.cars[i].id = response.cars[i].carID; }); return response; },
有时你会发现模型或集合需要在页面加载时进行初始化。许多关于Backbone.js模式的文章,例如Rico Sta Cruz的“Backbone模式”和Katz的“避免常见的Backbone.js陷阱”,讨论了这种模式。这种模式实现很容易,只需在页面中内联一段脚本,通过你选择的服务端语言,将单个模型属性或者JSON形式的数据呈现出来。例如,在Rails语言中,我采用下面的一种方法:
// a single attribute var model = new Model({ hello: <%= @world %> }); // or to have json var model = new Model(<%= @hello_world.to_json %>);
很多时候,你会想知道是哪个模型属性验证失败了。例如,如果你有一个极其复杂的表单字段,你或许想知道哪一个模型属性验证失败了,以便于你可以将该属性对应的输入字段进行高亮显示。不幸的是,警告视图到底是哪个模型属性验证失败并没有直接集成于Backbone.js,但是你可以用其他不同的模式去处理这个问题。
一个通知视图是哪个模型属性验证失败的模式是,返回一个对象,其中包含某种标志,它详细的记录了验证失败的属性,就像下面这样:
// Inside your model validate: function(attrs) { var errors = []; if(attrs.a < 0) { errors.push({ 'message': 'Form field a is messed up!', 'class': 'a' }); } if(attrs.b < 0) { errors.push({ 'message': 'Form field b is messed up!', 'class': 'b' }); } if(errors.length) { return errors; } } // Inside your view this.model.on('invalid’, function(model, errors) { _.each(errors, function(error, i) { $(‘.’ + error.class).addClass('error'); alert(error.message); }); });
我的朋友Derick Bailey推荐了一个可替代的模式,就是为每个模型属性触发自定义的errors事件。它允许你的视图能够针对单独的属性绑定特定的error事件:
// Inside your model validate: function(attrs) { if(attrs.a < 0) { this.trigger(‘invalid:a’, 'Form field a is messed up!', this); } if(attrs.b < 0) { this.trigger(‘invalid:b’, 'Form field b is messed up!', this); } } // Inside your view this.model.on('invalid:a’, function(error) { $(‘a’).addClass('error'); alert(error); }); this.model.on('invalid:b’, function(error) { $(‘b’).addClass('error'); alert(error); });
这两种模式都各有利弊,你应该考虑清楚哪个模式对你的应用案例是最优的。如果你按照同样的方式处理所有失败的验证,那么第一个方法可能是最好的;如果你对每个模型属性有特定的UI变化,那么后一种方法会更好。
如果终端的模型或集合收到了无效的JSON,尽管终端返回了HTTP状态码200,模型或集合还是会触发error事件。这种问题经常发生于开发阶段在本地模拟JSON数据造成的。那么,有个好方法是抛出经过 JSON 验证器验证了的模拟JSON数据文件。或者使用IDE的插件来及时捕获格式错误的JSON数据。
这能够节省开发时间以及创建统一的模式来处理和可视化错误消息,而且它可以改善整个的用户体验。我之前开发的每一个Backbone.js应用中,我都会创建一个通用的警告视图:
var AlertView = Backbone.View.extend({ set: function(typeOfError, message) { var alert = $(‘.in-page-alert’).length ? $(‘.in-page-alert’): $(‘.body-alert’); alert .removeClass(‘error success warning’) .addClass(typeOfError) .html(message) .fadeIn() .delay(5000) .fadeOut(); } });
var alert = new AlertView(); this.model.on('error', function(model, error) { alert.set('TYPE-OF-ERROR', error); });
如果你正在开发一个单页面应用,切记要更新每个页面的标题。我写过一个简单的插件(Backbone.js Router Title Helper),它以简单、优雅的格式扩展了 backbone.js router 的功能。它通过一个 Map 对象来控制路由,键来代表路由函数的名字,值则映射到页面的标题。
Backbone.Router = Backbone.Router.extend({ initialize: function(options){ var that = this; this.on('route', function(router, route, params) { if(that.titles) { if(that.titles[router]) document.title = that.titles[router]; else if(that.titles.default) document.title = that.titles.default; else throw 'Backbone.js Router Title Helper: No title found for route:' + router + ' and no default route specified.'; } }); } });
当谈论单页应用时,缓存对象的模式会经常用到!下面的例子简明扼要:
// Inside a router initialize: function() { this.cached = { view: undefined, model: undefined } }, index: function(parameter) { this.cached.model = this.cached.model || new Model({ parameter: parameter }); this.cached.view = this.cached.view || new View({ model: this.cached.model }); }
// Inside a router initialize: function() { this.cached = { view: undefined, model: undefined } }, index: function(parameter) { this.cached.model = this.cached.model || new Model({ parameter: parameter }); this.cached.view = this.cached.view || new View({ model: this.cached.model }); this.cached.model.fetch(); }
// Inside a router initialize: function() { this.cached = { view: undefined, model: undefined } }, index: function(parameter) { this.cached.model = this.cached.model || new Model({ parameter:parameter }); this.cached.model.set('parameter', parameter); this.cached.view = this.cached.view || new View({ model: this.cached.model }); } // Inside of the model initialize: function() { this.on("change:parameter", this.fetchData, this); }
我是文档注释和JSDoc的超级粉丝。我用JSDoc对所有的Backbone类添加了文档注释:
var Thing = Backbone.View.extend(/** @lends Thing.prototype */{ /** @class Thing * @author Phillip Whisenhunt * @augments Backbone.View * @contructs Thing object */ initialize() {}, /** Gets data by ID from the thing. If the thing doesn't have data based on the ID, an empty string is returned. * @param {String} id The id of get data for. * @return {String} The data. */ getDataById: function(id) {} });
我认为如果你使用Backbone.js开发项目,那么你应遵循测试驱动开发TDD。我第一次用Jasmine.js创建模型和集合时遵循TDD进行单元测试,但失败了。一旦写下单元测试并且失败,我会对整个模型和集合进行重写。
通过这一点,我的所有Jasmine测试都通过了,而且我有信心我的模型及集合会和我期望的一样工作。自从我遵循TDD,我的视图层非常容易编写而且非常简单。当你开始用TDD时,你的开发速度当然会很慢;但是一但你的脑海里一直想着TDD,你的编程效率和质量会显著提高。