本文爲翻譯文章:
譯者:Jacky <[email protected]>
譯者博客:http://blog.csdn.net/varkychan
原文:http://guides.sproutcore.com/getting_started.html
譯者前言:由於國內關於SproutCore的中文資源比較少,所以我開始嘗試翻譯官方網站的相關教程及指南,因爲本人英文水平真的一般,語文水平也不咋的,所以在譯文可能表達得不清晰,甚至是誤譯的可能,所以發現問題,請不嗇向我指點,吾將非常感激!
SproutCore入門教程 - 第一部分
入門
讀完本章後,你將能夠:
- 使用SproutCore的模板描繪你的應用界面
- 委託處理視圖(view)用戶事件
- 當模型(models)發生改變時通過綁定(bindings)更新視圖(views)
通過使用SproutCore從頭開始創建一個真實的“代辦事項(Todo list)”應用程序,你將學到所有這些內容。
1 跟隨
你可以在Github上看到這個應用程序最後完成的源代碼。要看運行的效果,點擊這裏。
這裏還有一個截屏視頻放在Vimeo上。
2 安裝SproutCore
這個指南假設你已經安裝了SproutCore。如果你還沒安裝,請現在安裝它。
這份指南需要安裝SproutCore 1.6.0 beta版。安裝程序在Windows和Mac上都有提供,如果你通過RubyGems來安裝,就需要使用 gem install sproutcore --pre 來安裝beta版本。
3 創建新應用
首先,通過如下命令生成一個基於HTML的新應用。
$ sc-init todos --template
這裏會創建幾個應用程序需要用到的文件,放在 apps/todods 文件夾內。
- apps/
-
- todos/
- todos.js – 在這裏定義model,view及controller.
- resources/
- templates/ – 將所有的Handlebars模板放在這裏.
- todos.handlebars – 應用程序的主模板.
- images/ – 圖片文件放在這裏.
- stylesheets/ – 樣式(CSS)文件放在這裏.
- todos.css – 應用程序的主樣式文件.
- templates/ – 將所有的Handlebars模板放在這裏.
- tests/
- todos/
- Buildfile – 告訴SproutCore如何創建你的應用程序. 通常, 你只要使用默認提供的即可.
- README – 對項目的說明.
現在打開todos.js文件. 你將會看到如下代碼:
apps/todos/todos.js
Todos = SC.Application.create();
SC.ready(function() {
Todos.mainPane = SC.TemplatePane.append({
layerId: "todos",
templateName: "todos"
});
});
代碼中爲你的應用程序創建了一個命名空間Todos,然後增加一個新的pane模板。pane負責事件委派和把模板放到DOM中,你會在後面的內容中瞭解到更多關於這些方面的內容。
如果你向sc-init傳遞一個駝峯式的名字,如ToDos(注意這裏的大寫字母D),這時命名空間是ToDos,而文件夾的名稱則是將命名空間進行下劃線分隔,如apps/to_dos。
4 定義模型(model)
在這個教程中,我們要創建一個列表,用於管理代辦事項(todos)。用戶能夠創建某個任務的新代表事項,然後在任務完成後可以剔除它。
首先讓我們定義一個model,它是由SC.Object派生出來的新子類:
apps/todos/todos.js
Todos = SC.Application.create();
Todos.Todo = SC.Object.extend({
title: null,
isDone: false
});
SC.ready(function() {
Todos.mainPane = SC.TemplatePane.append({
layerId: 'todos',
templateName: 'todos'
});
});
請在建立Todos對象的代碼下面插入新代碼。
現在我們已經定義了一個具有兩個屬性的類,這兩個屬性分別是:title:字符串類型;isDone:布爾類型。
5 使用Controller管理Model
現在我們知道我們的數據是怎樣的了,然後我們創建一個controller來管理它。因爲我們想要管理一個有序的代辦事項列表,因此我們將使用SC.ArrayController來實現它。
in apps/todos/todos.js
// 在文件未尾加入以下代碼
Todos.todoListController = SC.ArrayController.create({
// 以一個空數組初始化數組控制器.
content: []
});
在像 SproutCore 這樣的MVC框架中,controller層是作爲模型(model)層(只與表示數據的對象相關)和視圖(view)層(只與這些對象的顯示相關)的溝通橋樑。
現在已經有了一個沒有數據的數組控制器,讓我們增加一個創建新代辦事項的方法:
in apps/todos/todos.js
// 更新上面的代碼
Todos.todoListController = SC.ArrayController.create({
// 以一個空數組初始化數組控制器.
content: [],
// 用參數title創建一個新代辦事項,然後增加到數組中
createTodo: function(title) {
var todo = Todos.Todo.create({ title: title });
this.pushObject(todo);
}
});
SC.ArrayController 扮演content數組的代理角色,對ArrayController所作的修改會反映到content數組上。
6 用文本框(Text Field)創建新代辦事項
我們已經提供了一個簡單的樣式文件,用於對應用程序進行一些裝飾。你應該下載這個CSS文件,然後把sc-init爲我們生成的空文件 apps/todos/resources/stylesheets/todos.css 替換掉。
目前我們已經建立了model和controller,現在讓我們進入到有趣的部分:爲我們的用戶創建界面。第一步就是創建一個文本框,用於讓用戶輸入代辦事項的內容。SproutCore的TemplateView使用Handlebars模板來快速定義應用程序的界面。雖然Handlebars可以容易快速的標註HTML代碼,但你將會看到,它已經得到擴展,你只需要花很少的努力就能夠充分發揮它的優勢。
在架設界面之前,先讓我們打開 resources/templates/todos.handlebars 文件。你將會看到初始位居於文件中的HTML片段:
apps/todos/resources/templates/todos.handlebars
<h1>Welcome to SproutCore!</h1>
用下面的HTML代碼替換它:
apps/todos/resources/templates/todos.handlebars
<h1>Todos</h1>
<input id="new-todo" type="text"
placeholder="What needs to be done?" >
要獲得更多關於Handlebars的信息,請訪問Handlebars網站。要學習更多關於在SproutCore中使用Handlebars的信息,請閱讀使用Handlebars模板指南。
現在我們已經有了model,view及controller,是時候在瀏覽器中打開我們的程序,然後看看它會是怎麼樣的。
在開發過程中,sc-server讓測試應用程序變得非常簡單,只需要在項目文件夾裏運行以下命令即可:
$ sc-server
Starting server at http://0.0.0.0:4020 in debug mode
To quit sc-server, press Control-C
>> Thin web server (v1.2.1 codename Bat-Shit Crazy)
>> Maximum connections set to 1024
>> Listening on 0.0.0.0:4020, CTRL+C to stop
然後打開網頁瀏覽器訪問http://localhost:4020/todos地址,你將會看到應用程序在加載,只要你確認應用程序已經啓動並運行,就可以開始分析SproutCore是如何處理<input>標籤(上面加入的文本框)的事件了。當用戶在文本框中輸入內容並按回車鍵,程序就會創建一個新的代辦事項,然後插入到控制器的content數組中。
在SproutCore中,視圖對象是負責更新DOM和處理事件的。除此之外,它還允許我們緩存對DOM作出的改動以獲得最大的性能,以及爲常見的跨平臺事件處理提供支持。無論何時,只要你想顯示動態內容或處理事件,你就會用到視圖對象。
in apps/todos/todos.js
// 在 SC.ready 之前
Todos.CreateTodoView = SC.TextField.extend({
insertNewline: function() {
var value = this.get('value');
if (value) {
Todos.todoListController.createTodo(value);
this.set('value', '');
}
}
});
由於 CreateTodoView 中包含一個文件框,所以我們創建一個 SC.TextField 的子類,它爲我們提供了幾個方便使用文本框的方式。例如,你可以訪問 value 屬性、在用戶按下回車鍵時響應如 insertNewLink 這樣的高層事件。
界面已經定義好了,現在我們需要把它加入到Handlebars模板的HTML代碼中去。
像下面這樣把<input>標籤包起來:
apps/todos/resources/templates/todos.handlebars
<h1>Todos</h1>
{{#view Todos.CreateTodoView}}
<input id="new-todo" type="text"
placeholder="What needs to be done?" />
{{/view}}
#view是Handlebars的區塊助手程序(block helper),它給SC.TemplateView分配一塊HTML代碼。這就意味着在特定視圖中描述的行爲,如事件處理,將與區塊(block)中的HTML關聯。
現在我們有了創建代辦事項的界面,接下來我們建立一個界面把創建的代辦事項顯示出來。我們將使用到Handlebars的#collection助手程序(helper)來顯示一個代辦事項列表。#collection將創建一個SC.TemplateCollectionView實例,這個實例會使用內附的HTML代碼來呈現它的基礎數組(underlying array)中的每個條目。
in apps/todos/resources/templates/todos.handlebars
<!-- 在文件的末尾 -->
{{#collection SC.TemplateCollectionView ¬
contentBinding="Todos.todoListController"}}
{{content.title}}
{{/collection}}
我們使用了後續符( ¬)來標明不要回車的代碼行。(說明:爲了印刷的需要,一行太長的代碼行要分成多行顯示,而這行代碼必須在一行內完成,不能分行,故使用後續符來標明本行與接下來的一行是同一行代碼)。
注意,我們還讓collection將屬性content與todoListController控制器進行了綁定。對於數組控制器中的每個項目,collection都會創建一個新的子視圖(child view)去顯示模板{{content.title}}的內容。
你可以通過創建一個名字以Binding結尾的屬性來建立綁定關係。通過這種方式,我們將Todos.todoListController和collection視圖的content屬性進行了綁定。當綁定的其中一端發生了改變,SproutCore就會自動更新另外一端。
現在最好再去瀏覽一下 http://localhost:4020/todos(或者刷新一下,如果你還打開着),你會發現它看起來跟之前沒有什麼變化。試試在文本框中輸入一些文本然後按回車。看到了嗎?一旦我們創建了一條新代辦事項並插入到數組控制器中,視圖就會立即自動更新。
你現在已經看到SproutCore的小小威力了。通過使用SproutCore的綁定功能把數據和視圖建立起關係,你就只需要對數據層進行操作而把更新視圖層的艱苦工作留給SproutCore爲你完成。
事實上這就是SproutCore的一個核心概念,而不是演示的效果。SproutCore綁定系統的設計是從視圖系統的考慮出發的,這使得你只要直接對數據進行操作而不需要操心於手動保持視圖層同步。你會在這個指南的其它部分或其它教程中反覆看到這個概念。
7 把事情做完
現在已經可以增加代辦事項了,但卻還不能把它標記爲完成。爲了舒緩因此而產生的無終止的代辦事項使我們產生的挫敗感,先讓我們爲它增加能夠標記代辦事項爲完成狀態的功能。
首先要做的就是在每個代辦事項上增加一個複選框(checkbox),如前面所提到的,如果想要處理像用戶輸入這樣的事件,就需要一個視圖來管理HTML部分。既然如此,我們就添加一個複選框(checkbox)並且在用戶對它的值作出更改時獲得通知。記住,我們可以在模板中使用#view助手程序來分配視圖並提供HTML內容。而且,我們還能夠使用view助手程序來引用爲其本身提供HTML內容的視圖 (注意,沒有#號)。例如,我們可能想要創建一個相對複雜的可被重複利用的視圖,還有在需要更新的時候我們不想更新全部的模板。在todos.handlebars中,更新後的代辦事項看起來像這樣:
in apps/todos/resources/templates/todos.handlebars
<!-- 替換前面的代碼 -->
{{#collection SC.TemplateCollectionView ¬
contentBinding="Todos.todoListController"}}
{{view Todos.MarkDoneView}}
{{/collection}}
現在讓我們實現剛剛提到的Todos.MarkDoneView視圖。因爲視圖中實現了一個複選框,所以我們需要從SC.Checkbox控件派生一個新子類。它給視圖提供一個value屬性以反映DOM中的值,一個title屬性以顯示覆選框的標籤。
深入分析,SproutCore向change事件綁定一個處理程序,然後當事件發生時更新value屬性值。爲了兼容不同的瀏覽器,這種情況可能會變,但如果你只與SproutCore屬性打交道,那你就不用擔心這些問題了。
對於SC.TemplateCollectionView基礎數組內的每個項目,SC.TemplateCollectionView會創建一個新的子視圖,子視圖中的content屬性包含視圖要顯示的對象。在我們的例子中,每個代辦事項就是一個子視圖,然後每個子視圖的content屬性都設置了對應的Todo對象。
這樣就可以很容易對複選框中的屬性與我們呈現的Todo對象的屬性進行綁定。既然這樣,我們分別綁定複選框的value和title屬性到Todo的isDone和title屬性,以便某一方被更改時,另一方也自動更改。我們把它綁定起來:
in apps/todos/todos.js
// 在 SC.ready 之前
Todos.MarkDoneView = SC.Checkbox.extend({
titleBinding: '.parentView.content.title',
valueBinding: '.parentView.content.isDone'
});
在我們提供的樣式表中包含一個爲已完成的代辦事項提供獨立樣式的CSS class, 所以在你重新加載頁面查看效果之前,先讓我們也將每個項目的class與對象的isDone屬性綁定起來。我們通過使用collection助手程序的一個屬性來建立這個綁定。
in apps/todos/resources/templates/todos.handlebars
<!-- 替換前面的代碼 -->
{{#collection SC.TemplateCollectionView ¬
contentBinding="Todos.todoListController" ¬
itemClassBinding="content.isDone"}}
{{view Todos.MarkDoneView}}
{{/collection}}
在每個項目視圖上對這個屬性進行綁定定義,如果項目對應的content對象的isDone屬性是true,那它的class將是is-done。SproutCore會自動將屬性名破折分隔化(注:原文dasherize,即以破折號分隔屬性名,如isDone分隔成is-done)爲class名。
所有視圖都有很多屬性,包括id,class以及classBinding。collection助手程序允許你在這些屬性名前加上item字符,這樣就會應用到子項目的視圖。例如,如果在collection上使用itemClass屬性,那麼每個項目都會得到這個class。
現在在瀏覽器中重新加載應用程序,然後試一試。只要你一點擊某個代辦事項的複選框,那文字就會加上刪除線。請牢記,當你標記Todo爲已完成時,不需要你去更新任何視圖,綁定會爲你代勞。應用中任何一部分改變了Todo項目的isDone屬性,代辦列表會自動地更新,不需要你做任何工作。
8 你應該瞭解更多
現在我們可以創建代辦事項,並且在完成時標記爲已完成。然而76%的數據都是沒用的,試想想我們是否能夠從現有的數據中顯示有用的信息。例如在列表的頂部,我們可以顯示未完成的代辦事項條數。
打開todos.handlebars,然後插入如下新視圖:
in apps/todos/resources/templates/todos.handlebars
<!-- 在 Todos.CreateTodoView 之後 -->
{{#view Todos.StatsView id="stats"}}
{{displayRemaining}} remaining
{{/view}}
當視圖上的某個屬性發生改變時,像{{displayRemaining}} 這樣的Handlebars表達式允許我們自動更新DOM。這種情況下,Todos.StatsView的content屬性則應與視圖的displayRemaining屬性值進行綁定。跟其它的綁定一樣,無論何時它的值改變時,它都會自動爲我們更新。
我們現在接着繼續在todos.js實現這個視圖:
in apps/todos/todos.js
// 在 SC.ready 之前
Todos.StatsView = SC.TemplateView.extend({
remainingBinding: 'Todos.todoListController.remaining',
displayRemaining: function() {
var remaining = this.get('remaining');
return remaining + (remaining === 1 ? " item" : " items");
}.property('remaining')
});
displayRemaining包含一個複數形式的字符串,根據剩餘代辦事項的條數而定。以上代碼運行着SproutCore的另一個核心部分,一個稱作計算屬性(computed property)的概念。計算屬性是指它的屬性值是通過運行一個函數來決定的。例如,如果remaing等於1,則displayRemaining將是字符串”1 item”。
我們說displayRemaining依賴remaining,因爲它需要另一個值去產生它自己的值。我們把這些依賴的鍵名在property()定義中列出來。
我們還對視圖的remaining屬性和todoListController的remaining屬性進行了綁定,也就是說,如果Todos.todoListController.remaining發生了改變,那麼displayRemaining也會自動被更新。
當我們需要在視圖中顯示與代辦事項數據模型有關的信息時,最好是把它放到數組控制器中。現在讓我們在todos.js文件的todoListController控制器中加入一個新的計算屬性:
in apps/todos/todos.js
// 更新前面的代碼
Todos.todoListController = SC.ArrayController.create({
// ...
remaining: function() {
return this.filterProperty('isDone', false).get('length');
}.property('@each.isDone')
});
這裏,我們使用@each來指定屬性的依賴關係,它讓我們把屬性依賴於數組的每個項目上。這樣的話,當每條代辦事項的isDone屬性被改動時,remaining屬性就會更新。
當一條代辦事項被增加或刪除時,@each屬性也會更新。
定義計算屬性的依賴屬性非常重要,因爲SproutCore依據它去知道何時更新綁定屬性。既然這樣,那麼StatsView視圖在todoListController控制器的remaining屬性發生改變時就會更新。
讓我們看看它們是如何結合在一起的:
在我們創建應用程序時,我們就聲明瞭對象之間的鏈接關係,這些鏈接關係描述了應用的狀態是如何從模型層流向HTML表現層的。
9 清理已完成的代辦事項
除了在列表中填入代辦事項,我們也希望可以定期清理那些已經完成的項目。正如你所學到的,我們將會在todoListController控制器中對它進行清理,並讓SproutCore把這些操作所產生的變化自動地反映到DOM當中。
在控制器中增加一個新方法 clearCompletedTodos:
in apps/todos/todos.js
// 更新現有的代碼
Todos.todoListController = SC.ArrayController.create({
// ...
clearCompletedTodos: function() {
this.filterProperty('isDone', true).forEach(this.removeObject, this);
}
});
接下來,打開todos.handlebars文件,在模板中增加一個按鈕,如下面代碼所示,在StatsView視圖的HTML代碼中插入:
in apps/todos/resources/templates/todos.handlebars
<!-- 更新現有的代碼 -->
{{#view Todos.StatsView id="stats"}}
{{#view SC.Button classBinding="isActive" ¬
target="Todos.todoListController" ¬
action="clearCompletedTodos"}}
Clear Completed Todos
{{/view}}
{{displayRemaining}} remaining.
{{/view}}
我們已經定義了一個SC.Button實例,當它被點擊時會調用某個對象的一個方法。在這裏,我們讓它在點擊調用Todos.todoListController控制器(目標)對象的clearCompletedTodos方法(動作)。如果按鈕已經被點擊或輕敲一下(tapped),就讓它在class屬性中增加一個is-active類名,每個SC.Button按鈕都有一個isActive屬性,當按鈕正在被點擊時,它的值就爲true。這樣我們就可以向用戶顯示一個提醒效果,提示他們擊中目標。
切換到瀏覽器,再刷新試試看。添加一些代辦事項,然後把它們標記爲已完成,然後再清理它們。因爲我們先前就已經將可視列表與todoListController控制器進行了綁定,所以新加入的處理方式(即清理已完成代辦事項)所產生的改變也同樣得到預期的效果。
10 標記所有代辦事項都已完成
我們試想下,假設你已經把代辦事項中的所有工作都完成了,是否有好辦法可以一次性的把所有代辦事項都標記爲已完成?
事實上,根據我們應用程序定義的性質,所有困難的工作都已經完成。我們只需要再增加一個標記所有代辦事項爲已完成的操作即可。
首先,在控制器中創建一個新的計算屬性,用於標記是否所有的代辦事項都已完成。它看起來像這樣:
in apps/todos/todos.js
// 更新現有代碼
Todos.todoListController = SC.ArrayController.create({
// ...
allAreDone: function() {
return this.get('length') && this.everyProperty('isDone', true);
}.property('@each.isDone')
});
SproutCore有許多枚舉助手程序,如果數組中的對象有isDone屬性且值爲true,則everyProperty(‘isDone’,true)將返回true,否則返回false。你可以從Enumerables指南中找到更多相關信息。
接下來,我們創建一個複選框視圖,用於標記所有代辦事項爲完成的狀態,並將它的value屬性與控制器的allAreDone屬性進行綁定:
in apps/todos/resources/templates/todos.handlebars
<!-- 直接在 Todos.StatsView 下面插入-->
{{view SC.Checkbox class="mark-all-done" ¬
title="Mark All as Done" ¬
valueBinding="Todos.todoListController.allAreDone"}}
如果你已經刷新瀏覽器並開始使用這個程序,你可能已經注意到,當你逐個將所有代辦事項的複選框都勾選上後,“Mark All as Done”的複選框也變成已勾選狀態。然而,它還不能反方向操作,即點擊“Mark All as Done”時沒有任何效果。
目前爲止,我們的計算屬性已經描述瞭如何從依賴屬性中計算新的值。然而,在我們的程序中,我們想要接受一個新值,然後更新依賴的屬性來反映這個值。讓我們對allAreDone計算屬性進行修改,讓它也接受一個值。
in apps/todos/todos.js
// 更新現有代碼
Todos.todoListController = SC.ArrayController.create({
// ...
allAreDone: function(key, value) {
if (value !== undefined) {
this.setEach('isDone', value);
return value;
} else {
return this.get('length') && this.everyProperty('isDone', true);
}
}.property('@each.isDone')
});
當你設置一個計算屬性時,計算屬性函數會被調用,並會攜帶兩個參數,第一個參數key就是屬性鍵名,第二個參數value就是要設置的值。我們可以通過檢查value的值是否已定義來判別到底是讀屬性操作,還是寫屬性操作。如果value有定義,就遍歷代辦事項並將它的isDone屬性值設置爲value的值。
由於綁定是雙向的,當用戶點擊“Mark All as Done”複選框時,SproutCore會設allAreDone爲true,相反,取消對“Mark All as Done”複選框的勾選時,SproutCore會設allAreDone爲false,並取消對所有代辦事項的勾選。
重新加載應用程序,再添加一些代辦事項,然後點擊“Mark All as Done”,哇!每個代辦事項的複選框都被選上了。如果取消其中一個代辦事項的勾選,那麼“Mark All as Done”複選框就會取消選中。
你使用SproutCore時,當你擴展用戶界面時,你從不需要懷疑是否新的特性將與現有的用戶界面保持運作一致。因爲你的視圖層只是簡單的反映模型的狀態,你可以更改任何你想更改的東西,然後它們會自動更新。
11 移動設備特定樣式
當前,所有當代的移動設備都具備運行我們這個程序的功能。我們需要爲可能遇到的屏幕尺寸進行體驗優化。下載CSS for mobile樣式,然後保存到 apps/todos/resources/stylesheets/todos_mobile.css。然後在移動設備中訪問這個應用程序,你會看到同樣的應用,只是樣式適當地根據移動設備進行調整,並且響應觸摸事件。
你可以通過SproutCore開發服務器運行的機器的IP地址進行訪問,如 http://192.168.1.1:4020/todos。
12 資源
你已經完成了入門教程的第一部分,你可以從社區中獲得相關資源進行自學
- 加入 SproutCore郵件列表
- 在Twitter上關注 @sproutcore
- 訪問 #sproutcore IRC 頻道尋求實時幫助
- 把 SproutCore API Documentation 和 SproutCore Guides 加到收藏夾
13 繼續
你已經創建了一個基本的SproutCore應用程序,繼續進入本教程的下一部分,學習模型層的相關操作。