相信每个接触了解过backbone的人都知道todo,网上的关于它的分析教程也都分析乱了。但是,知识只有自己学习领悟才是自己的,话不多说,正文开始。
在分析todo的源码之前,首先我们要知道todo具有哪些功能。todo类似于备忘录一样,其界面如下图:
1.当我们在输入框完成输入item时,按下enter键,下方会出现输入的内容item,item是一个li。
2.在所有item的最上面是一个全选checkbox。
3.每一个item是一个checkbox,当勾选任一行item时,则该行的内容会被删除线划掉,同时最下面的item left-1,Clear completed item 1。
4.当鼠标移到item上时,item的末端会出现一个删除符号,点击删除符号,该item即被删除。
简短介绍完todo的基本功能后,开始源码之旅。
todo.js整体长这个样子,如下图:
一个model,一个collection,两个view。
model详见如下:
1 var Todo = Backbone.Model.extend({
2 defaults: function() {
3 return {
4 title: "empty todo...",
5 order: Todos.nextOrder(),
6 done: false
7 };
8 },
9 toggle: function() {
10 this.save({done: !this.get("done")});
11 }
12 });
默认设置defaults,返回一个list;定义了一个toggle,该触发器会设置保存model的状态。
collection详见如下:
1 var TodoList = Backbone.Collection.extend({
2 model: Todo,
3 localStorage: new Backbone.LocalStorage("todos-backbone"),
4 done: function() {
5 return this.where({done: true});
6 },
7 remaining: function() {
8 return this.where({done: false});
9 },
10 nextOrder: function() {
11 if (!this.length) return 1;
12 return this.last().get('order') 1;
13 },
14 comparator: 'order'
15 });
指定集合TodoList的mode为Todo,存储方式为localStorage,同时定义了done,remaining,nextOrder三个函数,最后指定了todo的排列顺序。
TodoView详见如下:
1 var TodoView = Backbone.View.extend({
2 tagName: "li",
3 template: _.template($('#item-template').html()),
4 events: {
5 "click .toggle" : "toggleDone",
6 "dblclick .view" : "edit",
7 "click a.destroy" : "clear",
8 "keypress .edit" : "updateOnEnter",
9 "blur .edit" : "close"
10 },
11 initialize: function() {
12 this.listenTo(this.model, 'change', this.render);
13 this.listenTo(this.model, 'destroy', this.remove);
14 },
15 render: function() {
16 this.$el.html(this.template(this.model.toJSON()));
17 this.$el.toggleClass('done', this.model.get('done'));
18 this.input = this.$('.edit');
19 return this;
20 },
21 toggleDone: function() {
22 this.model.toggle();
23 },
24 edit: function() {
25 this.$el.addClass("editing");
26 this.input.focus();
27 },
28 close: function() {
29 var value = this.input.val();
30 if (!value) {
31 this.clear();
32 } else {
33 this.model.save({title: value});
34 this.$el.removeClass("editing");
35 }
36 },
37 updateOnEnter: function(e) {
38 if (e.keyCode == 13) this.close();
39 },
40 clear: function() {
41 this.model.destroy();
42 }
43 });
AppView详见如下:
1 var AppView = Backbone.View.extend({
2 el: $("#todoapp"),
3 statsTemplate: _.template($('#stats-template').html()),
4 events: {
5 "keypress #new-todo": "createOnEnter",
6 "click #clear-completed": "clearCompleted",
7 "click #toggle-all": "toggleAllComplete"
8 },
9 initialize: function() {
10 this.input = this.$("#new-todo");
11 this.allCheckbox = this.$("#toggle-all")[0];
12 this.listenTo(Todos, 'add', this.addOne);
13 this.listenTo(Todos, 'reset', this.addAll);
14 this.listenTo(Todos, 'all', this.render);
15 this.footer = this.$('footer');
16 this.main = $('#main');
17 Todos.fetch();
18 },
19 render: function() {
20 var done = Todos.done().length;
21 var remaining = Todos.remaining().length;
22 if (Todos.length) {
23 this.main.show();
24 this.footer.show();
25 this.footer.html(this.statsTemplate({done: done, remaining: remaining}));
26 } else {
27 this.main.hide();
28 this.footer.hide();
29 }
30 this.allCheckbox.checked = !remaining;
31 },
32 addOne: function(todo) {
33 var view = new TodoView({model: todo});
34 this.$("#todo-list").append(view.render().el);
35 },
36 addAll: function() {
37 Todos.each(this.addOne, this);
38 },
39 createOnEnter: function(e) {
40 if (e.keyCode != 13) return;
41 if (!this.input.val()) return;
42 Todos.create({title: this.input.val()});
43 this.input.val('');
44 },
45 clearCompleted: function() {
46 _.invoke(Todos.done(), 'destroy');
47 return false;
48 },
49 toggleAllComplete: function () {
50 var done = this.allCheckbox.checked;
51 Todos.each(function (todo) { todo.save({'done': done}); });
52 }
53 });