相信每个接触了解过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 });