選擇正確的庫
創建一個JS APP沒有好的工具是很有難度的,jQuery只是操作DOM的庫,沒有提供任何創建APP的基礎,這就是為什麼我們要一個類似CanJS的專門的庫。
CanJS 是一個輕量級的MVC庫,提供你創建一個JS APP所需的工具。
CanJS 是一個輕量級的MVC庫,提供你創建一個JS APP所需的工具。 它提供有MVC (Model-View-Control) 模式的基本框架,模板動態綁定, route的支持且內存安全。同時支持jQuery, Zepto, Mootools, YUI, Dojo,有豐富的擴展和插件。
第一部分你將學到:
創建Control控制層和View 視圖層(UI模板) 來顯示聯繫人
用Model模型層來表示數據
使用fixtures 插件模擬ajax返回數據
你肯定激動了!我們開始碼代碼吧。
建立好你的文件夾和HTML
你先給你的APP創建一個文件夾,目錄下再建立4個子文件夾:css, js,views 和img。如下:
contacts_manager
css
js
views
img
保存以下的代碼為index.html:
複製代碼代碼如下:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>CanJS Contacts Manager</title>
<link rel="stylesheet" href="css/bootstrap.min.css">
<link rel="stylesheet" href="css/contacts.css">
</head>
<body>
<div>
<div>
<div>
<h1>Contacts Manager</h1>
</div>
</div>
<div>
<div>
<div>
<nav id="filter"></nav>
</div>
</div>
<div>
<div id="create"></div>
<div id="contacts"></div>
</div>
</div>
</div>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.js"></script>
<script src="js/can.jquery.min.js"></script>
<script src="js/can.fixture.js"></script>
<script src="js/contacts.js"></script>
</body>
</html>
在頁面的底部你加載所需的JS(包括你的APP:contacts.js)。
教程中用到的CSS和圖片文件可以下載。
用View來打造你的UI
View是用來渲染你APP的UI模板。 CanJS 支持多種模板引擎,本文用EJS ,CanJS包含有而且支持動態綁定。
EJS 模板的標籤與HTML很像,支持包含JS代碼,三種常用標籤如下:
<% CODE %> 執行JS
<%= CODE %> 執行JS,並將非轉義的結果寫入當前位置的HTML
<%== CODE %> 執行JS,並將轉義的結果寫入當前位置的HTML(用於子模板).
模板可以從文件或者script標籤中加載得到,本教程從EJS 文件加載。
顯示聯繫人
要創建聯繫人,你得先建立一個EJS 模板,保存以下代碼為contactsList.ejs 進你的views 文件夾:
複製代碼代碼如下:
<ul>
<% list(contacts, function(contact){ %>
<li <%= (el)-> el.data('contact', contact) %>>
<%== can.view.render('views/contactView.ejs', {
contact: contact, categories: categories
}) %>
</li>
<% }) %>
</ul>
contactLists.ejs 會渲染一個聯繫人列表,我們分析一下此模板:
複製代碼代碼如下:
<% list(contacts, function(contact){ %>
list()方法裡的回調方法如果配合配置有觀察者的list使用時,一旦list的數據發生改變就運用動態綁定重複調用。
複製代碼代碼如下:
<li <%= (el)-> el.data('contact', contact) %>>
以上代碼通過元素的回調方法生成一個有聯繫人數據的<li>。 箭頭後的方法執行後將el對象的數據設置給對應的元素。
複製代碼代碼如下:
<%== can.view.render('views/contactView.ejs', {
contact: contact, categories: categories
}) %>
以上代碼將子模板contactView.ejs 渲染成一個聯繫人。 can.view.render() 以模板和數據為參數返回HTML。
渲染單個聯繫人
子模板是一個將view組織成可管理塊的好辦法。 同時也使你的模板簡單和易於重用。教程後面將會用到此模板來創建聯繫人,將下面的代碼保存為contactView.ejs 進views 文件夾:
複製代碼代碼如下:
<a href="javascript://"><i></i></a>
<form>
<div>
<div>
<img src="img/contact.png">
</div>
<div>
<input type="text" name="name" placeholder="Add Name"
<%= contact.attr('name') ? "value='" + contact.name + "'" : "class='empty'" %>>
<select name="category">
<% $.each(categories, function(i, category){ %>
<option value="<%= category.data %>" <%= contact.category === category.data ? "selected" : "" %>>
<%= category.name %>
</option>
<% }) %>
</select>
</div>
<div>
<label>Address</label>
<input type="text" name="address"
<%= contact.attr('address') ? "value='" + contact.address + "'" : "class='empty'" %>>
<label>Phone</label>
<input type="text" name="phone"
<%= contact.attr('phone') ? "value='" + contact.phone + "'" : "class='empty'" %>>
<label>Email</label>
<input type="text" name="email"
<%= contact.attr('email') ? "value='" + contact.email + "'" : "class='empty'" %>>
</div>
</div>
</form>
聯繫人的屬性都放入了<input> 標籤裡,這就可以編輯更新用戶的資料。
活化你的View (好文藝。。)
EJS 處理模板過程中如果有用到attr() ,它周圍的代碼將會交由事件處理器管理,監聽對應屬性的變化,當屬性發生變化,APP中關聯的UI將會被更新。這功能利益於模板動態綁定機制,EJS的動態綁定是有選擇性的,只有使用了attr()時才會為對應的屬性開啟。
我們通過contactView.ejs 中一個<input>標籤來了解它的用法:
複製代碼代碼如下:
<input type="text" name="name" placeholder="Add Name"
<%= contact.attr('name') ? "value='" + contact.name + "'" : "class='empty'" %>>
特殊標記裡的代碼將轉變成事件綁定到此聯繫人的name屬性上。當name屬性發生變化,事件將被觸發同時HTML結構會被更新。
使用can.Control來處理業務邏輯
can.Control 創建了一個可組織,內在無洩漏,全權控制器,能用來創建widget或者處理業務邏輯。你通過所需要數據為一個DOM元素創建一個Control實例,可以在你的Control中定義方法綁定事件。
當Control 所關聯的元素從DOM被刪除時,Contol會自去銷毀自己,同時清除所綁定的方法。
要創建一個Control,通過傳入你定義的包含有函數的對像給can.Control() 來實現繼承。接下來事件也給傳進去了。
每個Contol實例都有幾個重要的值和方法規範:
this Control 實例的引用
this.element 實例中你所創建的DOM 元素
this.options 創建實例所需要的參數對象
init() 當實例創建成功時被調用
管理聯繫人
將以下代碼片段添加到contacts.js 文件來創建管理聯繫人的Control:
複製代碼代碼如下:
Contacts = can.Control({
init: function(){
this.element.html(can.view('views/contactsList.ejs', {
contacts: this.options.contacts,
categories: this.options.categories
}));
}
})
當Contacts 的實例被創建時, init() 會做兩件事:
使用can.view() 來渲染聯繫人。 can.view() 接收兩個參數:包含有模板和數據的文件或者stript標籤;將返回一個documentFragment (一個管理DOM元素的輕量容器)。
使用jQuery.html()將can.view() 的documentFragment 插入Control的元素
使用Model來表現數據
Model 是APP數據的抽象層。本APP用到兩個Model:一個對應聯繫人,一個對應類別。將以下代碼添加到contacts.js:
複製代碼代碼如下:
Contact = can.Model({
findAll: 'GET /contacts',
create : "POST /contacts",
update : "PUT /contacts/{id}",
destroy : "DELETE /contacts/{id}"
},{});
Category = can.Model({
findAll: 'GET /categories'
},{});
一個model 有5個方法可能定義來CRUD數據, 分別是findAll, findOne, create, update 和destroy。你可重寫這幾個方法,不過最好的辦法是使用REST 服務(Representational State Transfer表述性狀態轉移)。正如上面的代碼,你放心的忽略APP中不會用到的靜態方法了。
這裡要重點指出的是,model實例其實是源自CanJS 的'observables'。 can.Observe 提供對象的觀察者模式can.Observe.List 提供數組的觀察模式。這意味著你可以通過attr()來get和set數據,同時監聽數據的變動。
findAll() 方法返回一個Model.list,就是當元素被添加或者移除時can.Observe.List 所觸發的事件。
使用Fixture來模仿Rest
Fixture攔截AJAX 請求並通過文件或者方法來模擬應答。這對測試,或者後端還沒有就緒時是非常有用的。 Fixture就是APP的model模擬REST所需要的。
首先,你要準備一些數據給fixture,添加以下代碼到:
複製代碼代碼如下:
var CONTACTS = [
{
id: 1,
name: 'William',
address: '1 CanJS Way',
email: '[email protected]',
phone: '0123456789',
category: 'co-workers'
},
{
id: 2,
name: 'Laura',
address: '1 CanJS Way',
email: '[email protected]',
phone: '0123456789',
category: 'friends'
},
{
id: 3,
name: 'Lee',
address: '1 CanJS Way',
email: '[email protected]',
phone: '0123456789',
category: 'family'
}
];
var CATEGORIES = [
{
id: 1,
name: 'Family',
data: 'family'
},
{
id: 2,
name: 'Friends',
data: 'friends'
},
{
id: 3,
name: 'Co-workers',
data: 'co-workers'
}
];
有了數據,要將其連接到fixture來模擬REST 。 can.fixture()接收兩個參數。 我們要攔截的URL和我們應答用的文件和方法。通常你要攔截的URL都是動態且遵循一個模式的。在需要在URL裡添加以{}括起的通配符即可。
添加以下代碼到contacts.js:
複製代碼代碼如下:
can.fixture('GET /contacts', function(){
return [CONTACTS];
});
var id= 4;
can.fixture("POST /contacts", function(){
return {id: (id++)}
});
can.fixture("PUT /contacts/{id}", function(){
return {};
});
can.fixture("DELETE /contacts/{id}", function(){
return {};
});
can.fixture('GET /categories', function(){
return [CATEGORIES];
});
前4個fixture模擬Contact model的GET, POST, PUT 和DELETE 應答,第5個模擬Category model的GET應答。
啟動APP
你的APP有管理數據的Model,渲染聯繫人的View,將這一切組織起來的的Control。現在要做的就是啟動APP了。 Now you need to kickstart the application!
將以下代碼添加到contacts.js :
複製代碼代碼如下:
$(document).ready(function(){
$.when(Category.findAll(), Contact.findAll()).then(
function(categoryResponse, contactResponse){
var categories = categoryResponse[0],
contacts = contactResponse[0];
new Contacts('#contacts', {
contacts: contacts,
categories: categories
});
});
});
我們來分析一下這段代碼:
複製代碼代碼如下:
$(document).ready(function(){
使用jQuery.ready 方法監聽DOM的ready。
複製代碼代碼如下:
$.when(Category.findAll(), Contact.findAll()).then(
function(categoryResponse, contactResponse){
調用兩個Model的findAll() 方法來獲取全部聯繫人的類型,由於findAll() 有延時, $.when()則確保兩個請求同時完成後才執行回調方法。
複製代碼代碼如下:
var categories = categoryResponse[0],
contacts = contactResponse[0];
從兩個findAll() 方法中獲取對應Model實例的數據集。 是應答所返回的數組的第一個元素。
複製代碼代碼如下:
new Contacts('#contacts', {
contacts: contacts,
categories: categories
});
為#contacts 元素創建Contact 的Control 。聯繫人和類型數據集傳進Control。
用瀏覽器打開你的APP,你將看到如下的聯繫人列表:
總結
這是第教程系列的第一篇,你已經了解了CanJS的核心:
Models 你的APP數據的抽象層
Views 將數據轉換成HTML的模板
Controls 組織關聯一切