Backbone如何实现数据和视图的绑定呢?一方面,一个view可以将它的render()方法绑定到model的change事件,在模型变化时自动刷新视图。另一方面,当view发生变化时,需要call model的方法来保存变化。也就是视图发生变化时要主动更新模型,而模型发生变化时视图要通过监听事件来被动响应。在Backbone的术语中,有Model和View,但没有Controller,事实上,Backbone的View包含了controller的功能,Routers用来管理应用状态。更进一步地分析,发现Backbone实现了自己的MVP*架构,更像是MVP模型。
var Todo = Backbone.Model.extend({ defaults: { title: '', completed: false }, initialize: function () { console.log('This model has been initialized.'); this.on('change:title', function () { console.log('Title value for this model has changed.'); }); } }); var myTodo = new Todo(); myTodo.set('title', 'Check what\'s logged.');
var myTodo = new Todo({ title:'Read the whole book', id: 2 });
var TodosView = Backbone.View.extend({ tagName: 'ul', // required, but defaults to 'div' if not set className: 'container', // optional, you can assign multiple classes to // this property like so: 'container homepage' id: 'todos', // optional }); var todosView = new TodosView(); console.log(todosView.el); // logs <ul id="todos" class="container"></ul>
var todosView = new TodosView({ el: $('#footer') });实际情况中,View表示的html不可能这么简单,可以通过模板来实现复杂的渲染,这里包括两部分:模板和渲染(render)。前面说过Backbone并不限定模板引擎,所以可以选择任意自己喜欢的模板引擎,但本书样例采用Underscore的内置引擎。
app.TodoView = Backbone.View.extend({ //... is a list tag. tagName: 'li', // Cache the template function for a single item. template: _.template($('#item-template').html()), initialize: function () { this.listenTo(this.model, 'change', this.render); }, render: function () { this.$el.html(this.template(this.model.toJSON())); return this; } });
events: { 'click .toggle': 'toggleCompleted', 'dblclick label': 'edit', 'click .destroy': 'clear', 'blur .edit': 'close' }
var Todos = Backbone.Collection.extend({ model: app.Todo, });
var ourObject = {}; // Mixin _.extend(ourObject, Backbone.Events); // Add a custom event ourObject.on('dance', function (msg) { console.log('We triggered ' + msg); }); // Trigger the custom event ourObject.trigger('dance', 'our event');基本方法除了on(), off(), trigger(),还有listenTo()和stopListening(),后者可以让a对象监听b对象的事件,如
a.listenTo(b, 'anything', function (event) { console.log("anything happened"); });
var TodoRouter = Backbone.Router.extend({ /* 定义URL和回调函数的映射 */ routes: { "about": "showAbout", /* Sample usage: http://example.com/#about */ "todo/:id": "getTodo", /* This is an example of using a ":param" variable, which allows us to match any of the components between two URL slashes */ /* Sample usage: http://example.com/#todo/5 */ "search/:query": "searchTodos", /* We can also define multiple routes that are bound to the same map function, in this case searchTodos(). Note below how we're optionally passing in a reference to a page number if one is supplied */ /* Sample usage: http://example.com/#search/job */ "search/:query/p:page": "searchTodos", /* As we can see, URLs may contain as many ":param"s as we wish */ /* Sample usage: http://example.com/#search/job/p1 */ "todos/:id/download/*documentPath": "downloadDocument", /* This is an example of using a *splat. Splats are able to match any number of URL components and can be combined with ":param"s*/ /* Sample usage: http://example.com/#todos/5/download/todos.doc */ /* If you wish to use splats for anything beyond default routing, it's probably a good idea to leave them at the end of a URL; otherwise, you may need to apply regular expression parsing on your fragment */ "*other": "defaultRoute" /* This is a default route that also uses a *splat. Consider the default route a wildcard for URLs that are either not matched or where the user has incorrectly typed in a route path manually */ /* Sample usage: http://example.com/# <anything> */, "optional(/:item)": "optionalItem", "named/optional/(y:z)": "namedOptionalItem" /* Router URLs also support optional parts via parentheses, without having to use a regex. */ }, showAbout: function () {}, getTodo: function (id) {}, searchTodos: function (query, page) {}, downloadDocument: function (id, path) {}, defaultRoute: function (other) {} });
Backbone.sync = function(method, model, options) {};