在上一篇文章中我們把todos這個實例的數據模型進行了簡單的分析,有關于數據模型的操作也都知道了。接著我們來看剩下的兩個view的模型,以及它們對頁面的操作。
## 7.1 為什么要兩個view
首先要分析下,這倆view是用來干嘛的。有人可能會問了,這里不就是一個頁面嗎?一個view掌控全局不就完了?
我覺得這就是新手和老手的主要區別之一,喜歡在一個方法里面搞定一切,喜歡把東西都擰到一塊去,覺得這樣看起來容易。熟不知,這樣的代碼對于日后的擴展會造成很大的麻煩。因此我們需要學習下優秀的設計,從好的代碼中汲取營養。
這里面的精華就是,將對數據的操作和對頁面的操作進行分離,也就是現在代碼里面TodoView和AppView。前者的作用是對把Model中的數據渲染到模板中;后者是對已經渲染好的數據進行處理。兩者各有分工,TodoView可以看做是加工后的數據,這個數據就是待使用的html數據。
## 7.2 TodoView的代碼分析
TodoView是和Model一對一的關系,在頁面上一個View也就展示為一個item。除此之外,每個view上還有其他的功能,比如編輯模式,展示模式,還有對用戶的輸入的監聽。詳細還是來看下代碼:
~~~
// 首先是創建一個全局的Todo的collection對象
var Todos = new TodoList;
// 先來看TodoView,作用是控制任務列表
var TodoView = Backbone.View.extend({
//下面這個標簽的作用是,把template模板中獲取到的html代碼放到這標簽中。
tagName: "li",
// 獲取一個任務條目的模板,緩存到這個屬性上。
template: _.template($('#item-template').html()),
// 為每一個任務條目綁定事件
events: {
"click .toggle" : "toggleDone",
"dblclick .view" : "edit",
"click a.destroy" : "clear",
"keypress .edit" : "updateOnEnter",
"blur .edit" : "close"
},
//在初始化時設置對model的change事件的監聽
//設置對model的destroy的監聽,保證頁面數據和model數據一致
initialize: function() {
this.listenTo(this.model, 'change', this.render);
//這個remove是view的中的方法,用來清除頁面中的dom
this.listenTo(this.model, 'destroy', this.remove);
},
// 渲染todo中的數據到 item-template 中,然后返回對自己的引用this
render: function() {
this.$el.html(this.template(this.model.toJSON()));
this.$el.toggleClass('done', this.model.get('done'));
this.input = this.$('.edit');
return this;
},
// 控制任務完成或者未完成
toggleDone: function() {
this.model.toggle();
},
// 修改任務條目的樣式
edit: function() {
$(this.el).addClass("editing");
this.input.focus();
},
// 關閉編輯模式,并把修改內容同步到Model和界面
close: function() {
var value = this.input.val();
if (!value) {
//無值內容直接從頁面清除
this.clear();
} else {
this.model.save({title: value});
this.$el.removeClass("editing");
}
},
// 按下回車之后,關閉編輯模式
updateOnEnter: function(e) {
if (e.keyCode == 13) this.close();
},
// 移除對應條目,以及對應的數據對象
clear: function() {
this.model.destroy();
}
});
~~~
## 7.3 AppView的代碼分析
再來看AppView,功能是顯示所有任務列表,顯示整體的列表狀態(如:完成多少,未完成多少)
~~~
//以及任務的添加。主要是整體上的一個控制
var AppView = Backbone.View.extend({
//綁定頁面上主要的DOM節點
el: $("#todoapp"),
// 在底部顯示的統計數據模板
statsTemplate: _.template($('#stats-template').html()),
// 綁定dom節點上的事件
events: {
"keypress #new-todo": "createOnEnter",
"click #clear-completed": "clearCompleted",
"click #toggle-all": "toggleAllComplete"
},
//在初始化過程中,綁定事件到Todos上,
//當任務列表改變時會觸發對應的事件。
//最后從localStorage中fetch數據到Todos中。
initialize: function() {
this.input = this.$("#new-todo");
this.allCheckbox = this.$("#toggle-all")[0];
this.listenTo(Todos, 'add', this.addOne);
this.listenTo(Todos, 'reset', this.addAll);
this.listenTo(Todos, 'all', this.render);
this.footer = this.$('footer');
this.main = $('#main');
Todos.fetch();
},
// 更改當前任務列表的狀態
render: function() {
var done = Todos.done().length;
var remaining = Todos.remaining().length;
if (Todos.length) {
this.main.show();
this.footer.show();
this.footer.html(this.statsTemplate({done: done, remaining: remaining}));
} else {
this.main.hide();
this.footer.hide();
}
//根據剩余多少未完成確定標記全部完成的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, this);
},
//生成一個新Todo的所有屬性的字典
newAttributes: function() {
return {
title this.input.val(),
order: Todos.nextOrder(),
done: false
};
},
//創建一個任務的方法,使用backbone.collection的create方法。
//將數據保存到localStorage,這是一個html5的js庫。
//需要瀏覽器支持html5才能用。
createOnEnter: function(e) {
if (e.keyCode != 13) return;
if (!this.input.val()) return;
//創建一個對象之后會在backbone中動態調用Todos的add方法
//該方法已綁定addOne。
Todos.create({title: this.input.val()});
this.input.val('');
},
//去掉所有已經完成的任務
clearCompleted: function() {
// 調用underscore.js中的invoke方法
//對過濾出來的todos調用destroy方法
_.invoke(Todos.done(), 'destroy');
return false;
},
//處理頁面點擊標記全部完成按鈕
//處理邏輯:
// 如果標記全部按鈕已選,則所有都完成
// 如果未選,則所有的都未完成。
toggleAllComplete: function () {
var done = this.allCheckbox.checked;
Todos.each(function (todo) { todo.save({'done': done}); });
}
});
~~~
通過上面的代碼,以及其中的注釋,我們認識里面每個方法的作用。下面來看最重要的,頁面部分。
## 7.4 頁面模板分析
在前幾篇的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="view">
<input class="toggle" type="checkbox" <%= done ? 'checked="checked"' : '' %> />
<label><%- title %></label>
<a class="destroy"></a>
</div>
<input class="edit" type="text" value="<%- title %>" />
</script>
<script type="text/template" id="stats-template">
<% if (done) { %>
<a id="clear-completed">Clear <%= done %> completed <%= done == 1 ? 'item' : 'items' %></a>
<% } %>
<div class="todo-count"><b><%= remaining %></b> <%= remaining == 1 ? 'item' : 'items' %> left</div>
</script>
~~~
簡單的語法,上面的那個對應TodoView。有木有覺得比之前的那一版簡潔太多了,有木有!!啥叫代碼的美感,對比一下就知道了。
這一篇文章就先到此為止,文章中我們了解到在todos這個實例中,view的使用,以及具體的TodoView和AppView中各個函數的作用,這意味著所有的肉和菜都已經放到你碗里了,下面就是如何吃下去的問題了。
下一篇我們一起來學習todos的整個流程。
- 關于
- 前言
- 第一章 Hello Backbonejs
- 第二章 Backbonejs中的Model實踐
- 第三章 Backbonejs中的Collections實踐
- 第四章 Backbonejs中的Router實踐
- 第五章 Backbonejs中的View實踐
- 第六章 實戰演練:todos分析(一)
- 第七章 實戰演練:todos分析(二)View的應用
- 第八章 實戰演練:todos分析(三)總結
- 第九章 后端環境搭建:web.py的使用
- 第十章 實戰演練:擴展todos到Server端(backbonejs+webpy)
- 第十一章 前后端實戰演練:Web聊天室-功能分析
- 第十二章 前后端實戰演練:Web聊天室-詳細設計
- 第十三章 前后端實戰演練:Web聊天室-服務器端開發
- 第十四章 前后端實戰演練:Web聊天室-前端開發
- 第十五章 引入requirejs
- 第十六章 補充異常處理
- 第十七章 定制Backbonejs
- 第十八章 再次總結的說
- Backbonejs相關資源