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