在上一篇文章中我們把todos這個執行個體的資料模型進行了簡單的分析, 有關於資料模型的操作也都知道了。接著我們來看剩下的兩個view的模型,以及它們對頁面的操作。
首先要分析下,這個倆view是用來幹嘛的。按照自己的想法,一個頁面上的操作,直接用一個view來搞定不就行了嗎,為何要用兩個呢?
我覺得這就是新手和老手的主要區別之一,喜歡在一個方法裡面搞定一切,隨著時間的推移,再逐漸重構,讓代碼變得靈活可擴充。但既然我們拿到一個成熟的代碼,就應該吸取其中的精華。
我覺得這裡面的精華就是,將資料的展示和對資料的操作進行分離,也就是現在代碼裡面TodoView和AppView。前者的作用是展示資料模型中的資料到介面,並對資料本身進行管理;後者是對整體的一個控制,如所有資料的顯示(調用TodoView),添加一個任務、統計多少完成任務等。
有了上面的分析,讓我們來一起看下代碼:
// 首先是建立一個全域的Todo的collection對象 var Todos = new TodoList; // 先來看TodoView,作用是控制工作清單 var TodoView = Backbone.View.extend({ //下面這個標籤的作用是,把template模板中擷取到的html代碼放到這標籤中。 tagName: "li", // 擷取一個任務條目的模板 template: _.template($('#item-template').html()), // 為每一個任務條目綁定事件 events: { "click .check" : "toggleDone", "dblclick label.todo-content" : "edit", "click span.todo-destroy" : "clear", "keypress .todo-input" : "updateOnEnter", "blur .todo-input" : "close" }, //在初始化設定了todoview和todo的以一對一引用,這裡我們可以把todoview看作是todo在介面的映射。 initialize: function() { _.bindAll(this, 'render', 'close', 'remove'); this.model.bind('change', this.render); this.model.bind('destroy', this.remove); //這個remove是view的中的方法,用來清除頁面中的dom }, // 渲染todo中的資料到 item-template 中,然後返回對自己的引用this render: function() { $(this.el).html(this.template(this.model.toJSON())); this.input = this.$('.todo-input'); return this; }, // 控制任務完成或者未完成 toggleDone: function() { this.model.toggle(); }, // 修改任務條目的樣式 edit: function() { $(this.el).addClass("editing"); this.input.focus(); }, // 關閉編輯介面,並把修改內容同步到介面 close: function() { this.model.save({content: this.input.val()}); //會觸發change事件 $(this.el).removeClass("editing"); }, // 按下斷行符號之後,關閉編輯介面 updateOnEnter: function(e) { if (e.keyCode == 13) this.close(); }, // 移除對應條目,以及對應的資料對象 clear: function() { this.model.clear(); } });//再來看AppView,功能是顯示所有工作清單,顯示整體的列表狀態(如:完成多少,未完成多少)//以及任務的添加。主要是整體上的一個控制 var AppView = Backbone.View.extend({ //綁定頁面上主要的DOM節點 el: $("#todoapp"), // 在底部顯示的統計資料範本 statsTemplate: _.template($('#stats-template').html()), // 綁定dom節點上的事件 events: { "keypress #new-todo": "createOnEnter", "keyup #new-todo": "showTooltip", "click .todo-clear a": "clearCompleted", "click .mark-all-done": "toggleAllComplete" }, //在初始化過程中,綁定事件到Todos上,當工作清單改變時會觸發對應的事件。最後把存在localStorage中的資料取出來。 initialize: function() { //下面這個是underscore庫中的方法,用來Binder 方法到目前的這個對象中,是為了在以後運行環境中調用當前對象的時候能夠找到對象中的這些方法。 _.bindAll(this, 'addOne', 'addAll', 'render', 'toggleAllComplete'); this.input = this.$("#new-todo"); this.allCheckbox = this.$(".mark-all-done")[0]; Todos.bind('add', this.addOne); Todos.bind('reset', this.addAll); Todos.bind('all', this.render); Todos.fetch(); }, // 更改當前工作清單的狀態 render: function() { var done = Todos.done().length; var remaining = Todos.remaining().length; this.$('#todo-stats').html(this.statsTemplate({ total: Todos.length, done: done, remaining: remaining })); //根據剩餘多少未完成確定標記全部完成的checkbox的顯示 this.allCheckbox.checked = !remaining; }, //添加一個任務到頁面id為todo-list的div/ul中 addOne: function(todo) { var view = new TodoView({model: todo}); this.$("#todo-list").append(view.render().el); }, // 把Todos中的所有資料渲染到頁面,頁面載入的時候用到 addAll: function() { Todos.each(this.addOne); }, //產生一個新Todo的所有屬性的字典 newAttributes: function() { return { content: this.input.val(), order: Todos.nextOrder(), done: false }; }, //建立一個任務的方法,使用backbone.collection的create方法。將資料儲存到localStorage,這是一個html5的js庫。需要瀏覽器支援html5才能用。 createOnEnter: function(e) { if (e.keyCode != 13) return; Todos.create(this.newAttributes()); //建立一個對象之後會在backbone中動態調用Todos的add方法,該方法已綁定addOne。 this.input.val(''); }, // 去掉所有已經完成的任務 clearCompleted: function() { _.each(Todos.done(), function(todo){ todo.clear(); }); return false; }, //使用者輸入新任務的時候提示,延時1秒鐘 //處理邏輯是:首先擷取隱藏的提示節點的引用,然後擷取使用者輸入的值, //先判斷是否有設定顯示的延時,如果有則刪除,然後再次設定,因為這個事件是按鍵的keyup時發生的,所以該方法會被連續調用。 showTooltip: function(e) { var tooltip = this.$(".ui-tooltip-top"); var val = this.input.val(); tooltip.fadeOut(); if (this.tooltipTimeout) clearTimeout(this.tooltipTimeout); if (val == '' || val == this.input.attr('placeholder')) return; var show = function(){ tooltip.show().fadeIn(); }; this.tooltipTimeout = _.delay(show, 1000); }, //處理頁面點擊標記全部完成按鈕 //處理邏輯:如果標記全部按鈕已選,則所有都完成,如果未選,則所有的都未完成。 toggleAllComplete: function () { var done = this.allCheckbox.checked; Todos.each(function (todo) { todo.save({'done': done}); }); } });
通過上面的代碼,以及其中的注釋,我們應該認識了其中的各個函數的作用。但是有一點沒有說到的是template這個東西。
在前幾篇的view介紹中我們已經認識過了簡單的模板使用,以及變數參數的傳遞:
<script type="text/template" id="search_template"> <label><%= search_label %></label> <input type="text" id="search_input" /> <input type="button" id="search_button" value="Search" /></script>
既然能定義變數,那麼就能使用文法,如同django模板,那來看下帶有文法的模板,也是上面的兩個view用到的模板,我想這個是很好理解的。
<script type="text/template" id="item-template"> <div class="todo <%= done ? 'done' : '' %>"> <div class="display"> <input class="check" type="checkbox" <%= done ? 'checked="checked"' : '' %> /> <label class="todo-content"><%= content %></label> <span class="todo-destroy"></span> </div> <div class="edit"> <input class="todo-input" type="text" value="<%= content %>" /> </div> </div> </script> <script type="text/template" id="stats-template"> <% if (total) { %> <span class="todo-count"> <span class="number"><%= remaining %></span> <span class="word"><%= remaining == 1 ? 'item' : 'items' %></span> left. </span> <% } %> <% if (done) { %> <span class="todo-clear"> <a href="#"> Clear <span class="number-done"><%= done %></span> completed <span class="word-done"><%= done == 1 ? 'item' : 'items' %></span> </a> </span> <% } %> </script>
簡單的文法,上面的那個對應TodoView。
這一篇文章就先到此為止,文章中我們瞭解到在todos這個執行個體中,view的使用,以及具體的TodoView和AppView中各個函數的作用,這意味著所有的肉和菜都已經放到你碗裡了,下面就是如何吃下去的問題了。
下一篇我們一起來學習todos的整個流程。
本文連結: http://www.the5fire.com/8-backbone-todos-2.html