點(diǎn)燃圣火!Ember.js 的初學(xué)者指南
現(xiàn)在,到處都可以看到復(fù)雜的 JavaScript 應(yīng)用程序。 由于這些應(yīng)用程序變得越來越復(fù)雜,一長串的 jQuery 回調(diào)語句,或者通過應(yīng)用程序在各個(gè)點(diǎn)執(zhí)行不同的函數(shù)調(diào)用,這些都變得無法再讓人接受。 這導(dǎo)致了 JavaScript 開發(fā)人員了解到傳統(tǒng)的軟件程序員已經(jīng)知道了幾十年的問題: 組織和效率非常重要,并且可以對(duì)應(yīng)用程序的性能是否優(yōu)異產(chǎn)生重大影響。
實(shí)現(xiàn)組織和效率的其中一個(gè)最常用的架構(gòu)模式,被稱為 Model View Controller (縮寫為 MVC) 。 這種模式鼓勵(lì)開發(fā)人員將其應(yīng)用程序的不同部分分割為更易于管理的塊。 您不必使用一個(gè)函數(shù)直接調(diào)用數(shù)據(jù)庫,您可以創(chuàng)建了一個(gè) Model(模型)來為您管理數(shù)據(jù)庫。 您不必使用一個(gè)布滿輸出和邏輯語句的 HTML文件,一個(gè)簡單的模板或 View(視圖)就可以您簡化顯示代碼。 最后,Controller(控制器)管理您的應(yīng)用程序的流,幫助各種零散的部件更高效地互相溝通。 在您的應(yīng)用程序中使用這個(gè)模式,可以更輕松地增加新的功能。
作為最近爆發(fā)的基于 Internet 的軟件開發(fā)的一部分,出現(xiàn)了一堆令人眼花繚亂的 MVC 框架,比如 Ember.js、Backbone.js、Knockout.js、Spine.js、Batman.js 和 Angular.js。 一方面是初級(jí)和中級(jí)開發(fā)人員,另一方面是骨灰級(jí)程序員,以 JavaScript 編寫并針對(duì) JavaScript 開發(fā)而設(shè)計(jì)的這些庫補(bǔ)充了這兩者之間的空白。 它們提供多種特性和功能,根據(jù)開發(fā)人員的需求滿足技能水平各異的不同開發(fā)人員。
在本教程中,您將通過構(gòu)建一個(gè)可用的 Twitter 時(shí)間軸查看器,更熟悉 Ember.js。
Ember.js 簡介
Ember.js 是 JavaScript 框架包中最新的成員之一。 它演變出了最初于 2007 年創(chuàng)建的 SproutCore 項(xiàng)目,Apple 在包括 MobileMe 在內(nèi)的各種 web 應(yīng)用程序中大量使用了該項(xiàng)目。 在 emberjs.com,Ember 被形容為 "一個(gè) JavaScript 框架,用于創(chuàng)建可以消除樣板并提供標(biāo)準(zhǔn)應(yīng)用程序架構(gòu)的大型 web 應(yīng)用程序。" 它本身緊密集成了名為 Handlebars 的模板引擎,該引擎為 Ember 提供了其中一個(gè)最強(qiáng)大的功能: 雙向數(shù)據(jù)綁定。 Ember 還提供了其他功能,比如狀態(tài)管理(某個(gè)用戶狀態(tài)是已注銷還是已登錄)、自動(dòng)更新模板(當(dāng)?shù)讓訑?shù)據(jù)發(fā)生變化時(shí),您的 UI 也同樣發(fā)生變化)以及計(jì)算屬性 (firstName + lastName = fullName)。 Ember 經(jīng)過一年可靠的開發(fā)后,已經(jīng)成為一個(gè)強(qiáng)大的參與者。
Ember 只有一個(gè)依賴項(xiàng)—jQuery。 Ember 應(yīng)用程序的樣板 HTML 設(shè)置看起來應(yīng)該與下面的代碼類似。 請(qǐng)注意,jQuery 和 Ember 都從 CDN(內(nèi)容交付網(wǎng)絡(luò))進(jìn)行更新。 如果用戶在早些時(shí)候訪問需要這些文件的其他網(wǎng)站時(shí)已經(jīng)下載過這些文件,這會(huì)加快用戶的頁面加載速度。
- <html>
- <head>
- <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js">
- </script>
- <script src="http://cloud.github.com/downloads/emberjs/ember.js/ember-0.9.6.min.js">
- </script>
- <script src="js/app.js">
- </script>
- </head>
- <body>
- </body>
- </html>
定義 MVC
在您繼續(xù)本教程之前,更明確地定義 MVC 可能會(huì)是一個(gè)好主意。 這個(gè)概念在 1979 年已經(jīng)出現(xiàn),自那時(shí)以來,該模式已經(jīng)出現(xiàn)一些不同的變體。 最常見的流程通常是這樣的:
- 用戶執(zhí)行一個(gè)操作,比如敲擊鍵盤或單擊鼠標(biāo)按鈕。
- 控制器接收輸入并觸發(fā)一個(gè)消息給模型。
- 模型根據(jù)消息修改其內(nèi)容(刪除一行或更新購物車數(shù)量)。
- 視圖監(jiān)視模型中的變更,并相應(yīng)地更新用戶界面。
了解 MVC 模式的工作方式,可以使您的應(yīng)用程序流動(dòng)變得更簡單。 此外,由于代碼被分割成不同的塊,開發(fā)人員團(tuán)隊(duì)能夠更輕松地協(xié)同工作,且不互相干擾。
Ember 如何執(zhí)行 MVC
JavaScript 是一個(gè)靈活而強(qiáng)大的語言,但它也有不足之處。 它不提供那種使自己適合于 MVC 風(fēng)格開發(fā)的開箱即用的功能。 因此 Ember 利用一批額外功能擴(kuò)展了該基本語言。 在構(gòu)建 Ember 應(yīng)用程序時(shí),您會(huì)使用四個(gè)主要部件: Application(應(yīng)用程序)、Model(模型)、View(視圖)和 Controller(控制器)。 以下各節(jié)將回顧每個(gè)部件。
Application(應(yīng)用程序)
每個(gè) Ember 應(yīng)用程序都需要一個(gè) Ember.Application 實(shí)例。這是代碼其余所有部分的基礎(chǔ),它提供有用的功能,以及名稱空間(對(duì)應(yīng)用程序的其余部件進(jìn)行分組的一種方式)。 定義一個(gè) Ember 應(yīng)用程序很簡單:
- Songs = Ember.Application.create({ mixmaster: 'Andy' });
該代碼定義一個(gè)名為 Songs 的應(yīng)用程序,將其名為 mixmaster 的屬性設(shè)置為 Andy 。 您可以將應(yīng)用程序的名稱改為您喜歡的任何名稱,但 Ember 要求變量的名稱以一個(gè)大寫字母開始,以便綁定的系統(tǒng)可以找到它。 在創(chuàng)建應(yīng)用程序時(shí)還可以添加其他內(nèi)置選項(xiàng),并且您也可以添加任意的屬性或方法,但初學(xué)者用戶主要關(guān)心的可能是 ready() 方法。 該方法的工作方式與 jQuery 的 document.ready() 塊完全一樣,并且能夠通過以下方式實(shí)現(xiàn):
- Songs = Ember.Application.create({ mixmaster: 'Andy', totalReviews: 0, ready: function(){ alert('Ember sings helloooooooooo!'); } });
Models(模型)
如果沒有數(shù)據(jù),應(yīng)用程序就沒有意義。 Ember 使用 Models 幫助開發(fā)人員以結(jié)構(gòu)化的方式管理數(shù)據(jù)。 除了保存數(shù)據(jù)之外,Ember Models 也對(duì)其內(nèi)部的數(shù)據(jù)進(jìn)行建模。 換句話說,如果您想儲(chǔ)存有關(guān) MP3 集合的信息,您的模型可能包含一個(gè)標(biāo)題屬性、一個(gè)藝術(shù)家屬性和一個(gè)流派屬性等。 該模型可能看起來如下所示:
- Songs.Song = Ember.Object.extend({ title: null, artist: null, genre: null, listens: 0 });
關(guān)于這幾行代碼,還有幾件事情要注意。
- 您馬上就可以看到您的應(yīng)用程序在使用的名稱空間。 Songs 是應(yīng)用程序的名稱,而 Song 是模型的名稱。
- 當(dāng)擴(kuò)展對(duì)象時(shí),您是在為此模型的未來實(shí)例創(chuàng)建藍(lán)圖。 因?yàn)檫@是 主 因?yàn)檫@是主對(duì)象,所有歌曲將以它為基礎(chǔ),所以它使用一個(gè)大寫字母。 這些命名約定使得您在將來可以更輕松地分辨正在使用的對(duì)象類型。
- 在創(chuàng)建模型時(shí),您可以為每個(gè)屬性提供默認(rèn)值。 title、 artist 和 genre 屬性可在以后填寫,所以被標(biāo)記為 null (或無)。 listens 屬性默認(rèn)為 0 ,并且它的值將在您聽音樂收藏時(shí)增加。
現(xiàn)在, Song 模型已到位,您可以添加第一首歌曲。 您使用了 extend 來初始化 Song 模型,但您將使用 create 來添加它的一個(gè)實(shí)例。 下面是它的樣子:
- mySong = Song.create({ title: 'Son of the Morning', artist: 'Oh, Sleeper', genre: 'Screamo' });
請(qǐng)注意,變量沒有以一個(gè)大寫字母開始,那是因?yàn)樗?Song 模型的一個(gè)實(shí)例。 新的歌曲也不在 Songs 名稱空間中。 您將幾乎不再需要在您的應(yīng)用程序中創(chuàng)建模型的實(shí)例。 您這樣做當(dāng)然沒問題,但一般來說,您會(huì)將模型的每個(gè)實(shí)例放置在相近對(duì)象的較大集合中,比如 ArrayController(后面會(huì)詳細(xì)介紹)。
#p#
Views(視圖)
在一個(gè) Ember 應(yīng)用程序或任何 MVC 風(fēng)格的應(yīng)用程序中,View(視圖)是用戶可以看見并與之交互的組件。 通過將原始 HTML 直接添加到頁面,可以定義一個(gè)內(nèi)聯(lián)模板。 該模板將被包含在 script 標(biāo)記中。 您可以將它添加到頁面中您希望顯示內(nèi)容的任意位置。
- <script type="text/x-handlebars"> Hello <b>{{Songs.mixmaster}}</b> </script>
請(qǐng)注意, script 標(biāo)記的類型是 text/x-handlebars 。 這使 Ember 在加載頁面時(shí),有一些東西可以抓取。 Ember 自動(dòng)準(zhǔn)備在這個(gè)腳本標(biāo)記內(nèi)所包含的任何 HTML,以便在您的應(yīng)用程序中使用。 將這幾行代碼放在您的應(yīng)用程序中,就會(huì)顯示以下文本:
- Hello <b>Andy</b>
在繼續(xù)之前,先深入了解一下。 在您的瀏覽器中,右鍵單擊粗體文本,并使用瀏覽器的開發(fā)工具檢查它。 您可能注意到一些額外的元素。 為了知道在一個(gè)基本的屬性發(fā)生變化時(shí),要更新哪一部分的 HTML,Handlebars 將插入帶有唯一 ID 的標(biāo)記元素;例如:
- <b> <script id="metamorph-0-start" type="text/x-placeholder"></script> Andy <script id="metamorph-0-end" type="text/x-placeholder"></script> </b>
您可以直接在 JavaScript 中定義一個(gè)視圖,然后使用視圖輔助程序?qū)⑺@示到頁面中。 Ember 有通用視圖,可以在應(yīng)用程序中創(chuàng)建簡單的 div 標(biāo)記,但它還配有預(yù)打包的一組視圖,用于構(gòu)建基本控件,比如文本輸入框、復(fù)選框和選擇列表。 從在 JavaScript 文件中定義簡單的 TextArea 視圖開始。
- Songs.ReviewTextArea = Ember.TextArea.extend({ placeholder: 'Enter your review' });
然后,通過引用包含該視圖的變量的、以單詞 view 開頭的路徑,將它顯示到頁面。 運(yùn)行下面的代碼,在您的瀏覽器中顯示 TextArea 字段,其占位符文本為 "Enter your review"。 您也可以在定義中指定 rows 和 cols 作為額外的屬性。
- <script type="text/x-handlebars"> {{view Songs.ReviewTextArea}} </script>
Handlebars
現(xiàn)在,您可能想知道代碼中的 {{ 和 }} 表示什么,我們正好可以談?wù)?Handlebars,也稱為 mustaches。 稍微思考一下,您就會(huì)明白它們?yōu)槭裁幢环Q為 Handlebars pard'ner。 Handlebars 是一個(gè)模板引擎,讓開發(fā)人員可以混合原始 HTML 和 Handlebars 表達(dá)式 ,生成渲染的HTML。 表達(dá)式以 {{ 開始,并以 }} 結(jié)束。 如前所述,所有模板必須放在類型為 text/x-handlebars 的 script 標(biāo)記內(nèi)。
默認(rèn)情況下,handlebars 內(nèi)所包含的任何值據(jù)稱都會(huì)綁定到它的值。 這意味著,如果因?yàn)閼?yīng)用程序內(nèi)的一些其他操作使該值發(fā)生變化,顯示給用戶的值也將更新。 考慮以下代碼:
- <script type="text/x-handlebars"> My songs have {{Songs.totalReviews}} reviews. </script>
第一次初始化您的應(yīng)用程序時(shí),用戶將看到以下文本。
- My songs have 0 reviews.
但是,憑借數(shù)據(jù)綁定,隨著因更新 Songs.totalReviews
而添加的更多評(píng)論,該值將實(shí)時(shí)改變。
Handlebars 還通過使用 {{#if}}
和 {{else}}
. 支持流控制。 這些元素使您可以根據(jù)應(yīng)用程序中的值實(shí)現(xiàn)模板的 條件化 。 您可以修改前面的示例,在沒有任何評(píng)論時(shí)向用戶顯示另一條消息:
- <script type="text/x-handlebars"> {{#if Songs.totalReviews}} Read all my reviews! {{else}} There are no reviews right now. {{/if}} </script>
如果在應(yīng)用程序的生命周期中的任意時(shí)點(diǎn), Songs.totalReviews 值發(fā)生了變化,該視圖將更新并顯示另一部分的消息。 值得注意的還有, # 和 / 符號(hào)只是為了告訴 Handlebars,這個(gè)特定的視圖輔助程序有一個(gè)閉合標(biāo)記。
Controllers(控制器)
此前,Model(模型)被定義為一種使開發(fā)人員能夠管理數(shù)據(jù)的方法。 這沒錯(cuò),但這只是一種狹義的定義。 一個(gè)模型只包含與單一事物有關(guān)的數(shù)據(jù);例如,一首歌曲(但不是多首歌曲)或一個(gè)人(但不是多個(gè)人)。 當(dāng)您想管理多個(gè)相同類型的數(shù)據(jù)塊時(shí),您需要一個(gè) Controller(控制器)。 有了 Ember,您可以使用 ArrayController 來管理多組歌曲、人員、部件或任何東西。 每個(gè) ArrayController 都有內(nèi)置的 content 屬性,用于存儲(chǔ)數(shù)據(jù)。 該數(shù)據(jù)可以是簡單的字符串,也可以是復(fù)雜的值,比如數(shù)組或?qū)ο蟆?此外,ArrayController 中包含的函數(shù)可以用于與在 ArrayController 中所包含的數(shù)據(jù)進(jìn)行交互。 您的 Song 集合的 ArrayController 看起來會(huì)是什么樣呢?
- Songs.songsController = Ember.ArrayController.create({ content: [], init: function(){ // create an instance of the Song model var song = Songs.Song.create({ title: 'Son of the Morning', artist: 'Oh, Sleeper', genre: 'Screamo' }); this.pushObject(song); } });
init 函數(shù)不是必需的,但它很方便,因?yàn)?songsController 一旦就緒,就會(huì)觸發(fā) init 函數(shù)。 它可以用來將現(xiàn)有數(shù)據(jù)填充到控制器,在本例中,您將使用它來將一首歌曲添加到控制器中,以演示 Ember 的數(shù)據(jù)綁定。 添加之前的 ArrayController 定義和以下內(nèi)聯(lián)模板,并在您的瀏覽器中運(yùn)行代碼:
- <script type="text/x-handlebars"> {{#each Songs.songsController}} <h3>{{title}}</h3> <p>{{artist}} - {{genre}}</p> {{/each}} </script>
Handlebars each 輔助程序收到一組數(shù)據(jù)的路徑,然后對(duì)它進(jìn)行循環(huán)。 與控制器中每一項(xiàng)分別匹配的各個(gè) each 塊中的一切都將顯示在頁面上。 請(qǐng)注意,您沒有提供直接訪問內(nèi)容數(shù)組的路徑,因?yàn)榫?Ember 而言,控制器 就是 數(shù)組。所生成的 HTML 輸出如下所示:
- <h3>Son of the Morning</h3> <p>Oh, Sleeper - Screamo</p>
全面整合: EmberTweets
此時(shí),您應(yīng)該已較好地了解了 Ember 是什么,以及它可以做什么。您也應(yīng)該了解使 Ember 實(shí)現(xiàn)其能力的每個(gè)組件: Application(應(yīng)用程序)、Model(模型)、View(視圖)和 Controller(控制器)。 現(xiàn)在是時(shí)候應(yīng)用這些知識(shí)了,以編寫一個(gè)真實(shí)可用的應(yīng)用程序。 您將跳過行業(yè)標(biāo)準(zhǔn)的 "todo 應(yīng)用程序" ,并轉(zhuǎn)移到對(duì)許多人來說更親近和熟悉的: Twitter。 在本教程的剩余部分,您將構(gòu)建一個(gè) Twitter 時(shí)間軸查看器。 在編寫任何代碼之前, 看看最終結(jié)果可能會(huì)有幫助。
#p#
創(chuàng)建樣板文件
使用本文開頭的樣板 HTML 頁面,您將首先構(gòu)建基礎(chǔ) HTML。 復(fù)制下面的代碼,并粘貼到一個(gè)名為 index.html 的新 HTML 文件中。您需要引用在本文的示例文件中的 CSS 文件。 示例文件還包含了這個(gè)項(xiàng)目的起點(diǎn),也可以供您隨時(shí)使用。
- <!doctype html> <html>
- <head>
- <title>Tweets</title>
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <link rel="stylesheet" href="styles.css">
- <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
- <script src="http://cloud.github.com/downloads/emberjs/ember.js/ember-0.9.6.min.js"></script>
- <script src="app.js"></script>
- </head>
- <body>
- <script type="text/x-handlebars">
- <div id="frm"> <b>Load Tweets for: </b> </div>
- <div id="content">
- <div id="recent">
- <h3>Recent Users</h3>
- </div>
- <div id="tweets"> <h3>Tweets</h3> </div>
- </div>
- </script>
- </body>
- </html>
您可以看到這個(gè)應(yīng)用程序有三個(gè)部分: 一個(gè)輸入字段,允許用戶輸入一個(gè) Twitter 用戶名;時(shí)間軸查看器,顯示所選 Twitter 用戶的 tweet;以及最新的用戶列表,它將存儲(chǔ)以前的搜索。
搜索框?qū)⒊霈F(xiàn)在頁面頂部,最新的用戶在左側(cè)的欄中,而 tweet 本身將占據(jù)頁面右側(cè)的絕大部分版面。
下一步,創(chuàng)建名為 app.js 的另一個(gè)文件,并添加以下內(nèi)容。 這些注釋可以幫助您保持代碼的條理性。 在您的瀏覽器中加載此頁面,并確保沒有任何錯(cuò)誤。
- /************************** * Application **************************/
- /************************** * Models **************************/
- /************************** * Views **************************/
- /************************** * Controllers **************************/
請(qǐng)注意,此行使用的不是 Ember.Application,而是 Em.Application。在可能要使用 "Em" 任何地方,您都可以使用 "Ember",Ember 團(tuán)隊(duì)通過加入這個(gè)方便的快捷方式,減少了要輸入的字?jǐn)?shù)。
接下來,您將添加 TextInput 視圖和提交按鈕。 直接在標(biāo)記為 "Views" 的注釋塊下面,放入以下代碼:
- App = Em.Application.create();
請(qǐng)注意,此行使用的不是 Ember.Application,而是 Em.Application。在可能要使用 "Em" 任何地方,您都可以使用 "Ember",Ember 團(tuán)隊(duì)通過加入這個(gè)方便的快捷方式,減少了要輸入的字?jǐn)?shù)。
接下來,您將添加 TextInput 視圖和提交按鈕。 直接在標(biāo)記為 "Views" 的注釋塊下面,放入以下代碼:
- App.SearchTextField = Em.TextField.extend({ insertNewline: function(){ App.tweetsController.loadTweets(); } });
此塊從使用 App 名稱空間開始,然后擴(kuò)展 Ember 的其中一個(gè)預(yù)打包的視圖 TextField。 除了在 Views 內(nèi)允許任意屬性和函數(shù)之外,Ember 也提供了內(nèi)置的輔助函數(shù)。 那就是 insertNewLine()
函數(shù);每當(dāng)光標(biāo)在輸入框中,并且用戶在鍵盤上按 Enter/Return 鍵時(shí),就會(huì)執(zhí)行該函數(shù)。
創(chuàng)建模板塊
現(xiàn)在已定義了 TextField View,您將要添加相應(yīng)的視圖輔助代碼到 HTML 文件中。 切換到 index.html 并在顯示為 "Load Tweets for" 的行后面直接添加以下代碼。 請(qǐng)記住, {{
和 }}
內(nèi)的任何代碼都是模板,并且將被 Ember 用于輸出數(shù)據(jù)。 此外,任何以單詞 view
開始的模板指的都是已經(jīng)在您的 JavaScript 代碼中定義過的視圖。
- {{view App.SearchTextField placeholder="Twitter username" valueBinding="App.tweetsController.username"}} <button {{action "loadTweets" target="App.tweetsController"}}>Go!</button>
這部分的模板包含一個(gè)視圖輔助程序,以及一個(gè)帶有 {{action}} 輔助程序的按鈕標(biāo)記。 TextField View SearchTextField 以占位符文本開始,占位符文本是內(nèi)置在 HTML5 文本輸入字段中的一個(gè)屬性。 如果該字段為空, placeholder 屬性中的文本將被放進(jìn)輸入字段。 當(dāng)有人開始輸入時(shí),該值將消失。 Ember 使開發(fā)人員可以在其內(nèi)置視圖內(nèi)使用任何 HTML 5 標(biāo)準(zhǔn)屬性。
第二個(gè)屬性突出顯示 Ember 數(shù)據(jù)綁定的能力。 Ember 使用一組約定來幫助它確定您想完成什么。 視圖(一個(gè)模板或一個(gè) JavaScript 文件中的視圖)中以單詞 "Binding" (注意大寫字母)結(jié)尾的任何屬性,自動(dòng)為它前面的屬性設(shè)置綁定。 在本例中,Ember 將 App.tweetsController.username 的值綁定到輸入字段的 value 屬性。 每當(dāng)變量的內(nèi)容發(fā)生變化,輸入字段中的值將自動(dòng)更新,反之亦然。
{{action}} 使得更易于將功能添加到輸入驅(qū)動(dòng)的元素。 它有兩個(gè)選項(xiàng): 操作名稱和目標(biāo)。 兩者綜合起來,形成一個(gè) "路徑" ,指向 Ember 對(duì)象中所包含的函數(shù)。 在上面按鈕的示例中, "路徑" 將是 App.tweetsController.loadTweets() ,當(dāng)用戶在文本字段內(nèi)按 Enter 鍵時(shí),也會(huì)調(diào)用相同的函數(shù)。 在您的瀏覽器中加載 index.html,并單擊提交按鈕,或在輸入字段內(nèi)按 Enter 鍵。 如果您查看瀏覽器控制臺(tái),您就會(huì)看到一個(gè)錯(cuò)誤。 這是因?yàn)檫€沒有定義 App.tweetsController 。
準(zhǔn)備 Tweet 存儲(chǔ)對(duì)象
現(xiàn)在是定義 App.tweetsController 的好時(shí)機(jī)。 在 app.js 中的 Controllers 注釋塊后面添加以下代碼。您應(yīng)該已經(jīng)熟悉以下代碼。 名稱空間、ArrayController、內(nèi)容數(shù)組 —— 一切都在。 但這次您將會(huì)添加一個(gè)任意屬性 ( username ) 和一個(gè)函數(shù) ( loadTweets )。 在添加 ArrayController 之后,重新加載您的瀏覽器。 在輸入框中輸入一個(gè)單詞,然后單擊按鈕。 您會(huì)得到一個(gè)提示框,回顯您所鍵入的單詞。 您可以隨時(shí)刪除提示行。 您還將看到一個(gè)錯(cuò)誤,表明尚未定義 addUser 方法。
- App.tweetsController = Em.ArrayController.create({ c
- ontent: [], username: '', loadTweets: function() {
- var me = this; var username = me.get("username");
- alert(username);
- if ( username ) {
- var url = 'http://api.twitter.com/1/statuses/user_timeline.json' url += '?screen_name=%@&callback=?'.fmt(me.get("username"));
- // push username to recent user array App.recentUsersController.addUser(username);
- } } });
仔細(xì)看看 loadTweets
函數(shù)的定義;它有一些陌生的代碼。 第一行為函數(shù)的其余部分設(shè)置一個(gè) 范圍 。 根據(jù)定義,范圍或 this 對(duì)于所有 Ember 對(duì)象來說都是當(dāng)前函數(shù),在本例中是 App.tweetsController
。 但是,在本教程中,稍后您可以將更多功能添加到 loadTweets
函數(shù)。 現(xiàn)在設(shè)置當(dāng)前范圍,有助于 Ember 理解您正在使用的上下文。
如前所述,Ember 提供了許多輔助函數(shù),使編寫應(yīng)用程序變得更容易,這些函數(shù)包括 get()
和 set()
。 這兩個(gè)函數(shù)內(nèi)置在每個(gè) Ember 對(duì)象中,并提供對(duì)任何屬性或函數(shù)的快速訪問。 下一行使用當(dāng)前對(duì)象 App.tweetsController
的范圍,然后調(diào)用 get()
函數(shù),將您希望獲取其值的屬性的名稱傳遞進(jìn)去。 您可能會(huì)好奇,在開始時(shí)要使用的用戶名的值從哪里來。 請(qǐng)記住,Ember 的數(shù)據(jù)綁定是雙向的。 這意味著,一旦您在輸入字段中鍵入了一個(gè)值,該輸入字段視圖的 valueBinding
屬性就會(huì)用一個(gè)值更新 App.tweetsController
對(duì)象。
檢索到用戶名后,會(huì)運(yùn)行一個(gè)測試,以確保它不是空的。 此時(shí) if
塊內(nèi)只有兩個(gè)語句,但這在以后會(huì)更改。 第一個(gè)語句為一個(gè)用戶將 URL 設(shè)置到 Twitter 的 JSON 文件。 您可能沒有馬上注意到這里有什么特殊之處,但仔細(xì)再看看,您就會(huì)發(fā)現(xiàn)末尾的 %@
和 .fmt()
。 .fmt()
函數(shù)執(zhí)行一個(gè)方便的字符串替換,使用 %@
作為標(biāo)記。 由于應(yīng)用程序的設(shè)計(jì)要求存儲(chǔ)搜索列表,因此您必須以某種方式存儲(chǔ)您的搜索字詞。 最后一行執(zhí)行該函數(shù),將用戶名的值推送到 App.recentUsersController
ArrayController。 因?yàn)樵搶?duì)象尚未存在,所以運(yùn)行代碼將產(chǎn)生一個(gè)錯(cuò)誤。
#p#
存儲(chǔ)之前的搜索
在下面這一節(jié)中,您將創(chuàng)建用于存儲(chǔ)最近搜索的對(duì)象。 使用以下代碼并將它添加到 App.tweetsController
對(duì)象后面。
- App.recentUsersController = Em.ArrayController.create({
- content: [], addUser: function(name) {
- if ( this.contains(name) )
- this.removeObject(name);
- this.pushObject(name); }, removeUser: function(view)
- { this.removeObject(view.context); }, searchAgain:
- function(view){ App.tweetsController.set('username', view.context); App.tweetsController.loadTweets(); },
- reverse: function(){ return this.toArray().reverse(); }.property('@each') });
您已經(jīng)熟悉了如何創(chuàng)建 ArrayController 和添加一個(gè)空的內(nèi)容數(shù)組,但該對(duì)象有一些新的元素,它們以 addUser 函數(shù)開始。 這將使用名稱為 contains() 的內(nèi)置 Ember 函數(shù)檢查現(xiàn)有數(shù)組 ( this )。 如果它找到一個(gè)結(jié)果,它會(huì)使用 ArrayController 的函數(shù) removeObject() 刪除該結(jié)果。 與該函數(shù)相反,有一個(gè)名為 pushObject() 的函數(shù),它用于將單獨(dú)的對(duì)象添加到內(nèi)容數(shù)組。 這兩個(gè)函數(shù)都有處理多個(gè)對(duì)象的復(fù)數(shù)版本: pushObjects() 和 removeObjects() 。 此代碼在添加搜索詞之前首先刪除現(xiàn)有搜索詞,那么相同的搜索詞就不會(huì)被多次顯示。 既然您已經(jīng)知道如何從內(nèi)容數(shù)組中刪除一個(gè)對(duì)象, removeUser() 函數(shù)中唯一的新元素就是參數(shù)。 當(dāng)使用 {{action}} 輔助程序調(diào)用一個(gè)函數(shù)時(shí),Ember 隱式傳遞一個(gè)引用給當(dāng)前視圖。 在 App.tweetsController 的示例中,視圖有一個(gè)上下文,這基本上是當(dāng)前被遍歷的項(xiàng)。 該上下文用于從數(shù)組中刪除選定的項(xiàng)。
searchAgain() 函數(shù)也將當(dāng)前視圖接收為一個(gè)參數(shù)。 當(dāng)用戶單擊之前搜索過的字詞,該函數(shù)會(huì)用選定的用戶名填充 App.tweetsController.username ,然后觸發(fā) loadTweets() 函數(shù),為前面的搜索提供一個(gè)單擊視圖。
默認(rèn)情況下,Ember 按升序?qū)?nèi)容顯示到頁面。 數(shù)組索引 1 是第一個(gè),數(shù)組索引 2 是第二個(gè),依次類推。 此應(yīng)用程序的設(shè)計(jì)要求按降序顯示最近的搜索。 這意味著該數(shù)組必須被逆轉(zhuǎn)。 雖然這不是內(nèi)置函數(shù),但您可以看到要添加它是多么容易。 Reverse() 首先使用 Ember toArray() 函數(shù)將 Ember 內(nèi)容數(shù)組轉(zhuǎn)換成一個(gè)普通的數(shù)組,逆轉(zhuǎn)它,然后返回它。 使得該函數(shù)可以被用作一個(gè)數(shù)據(jù)源的,是在末尾添加的 property() 函數(shù)。 property() 函數(shù)使用一個(gè)指定函數(shù)所要求的以逗號(hào)分隔的屬性列表。 在本例中, property() 函數(shù)隱式地使用內(nèi)容數(shù)組本身,使用 @each 依賴鍵對(duì)該數(shù)組內(nèi)每個(gè)元素進(jìn)行尋址。 在下一節(jié)中,您將看到如何實(shí)施 reverse() 函數(shù)。
顯示之前的搜索
現(xiàn)在,您已存儲(chǔ)了之前的搜索,是時(shí)候?qū)⑺鼈冿@示在頁面上。 復(fù)制以下模板,并將它添加到標(biāo)簽為 Recent Users 的 h3 標(biāo)記后面。
- <ol> {{#each App.recentUsersController.reverse}} <li> <a href="#" title="view again" {{action "searchAgain" target="App.recentUsersController"}}>{{this}}</a> - <a href="#" title="remove" {{action "removeUser" target="App.recentUsersController"}}>X</a> </li> {{/each}} </ol>
現(xiàn)在,您應(yīng)該已經(jīng)熟悉所有這些代碼。 each
塊指向內(nèi)容數(shù)組,它內(nèi)部所包含的 HTML 將被應(yīng)用到 App.recentUsersController
變量中的每一項(xiàng)。 不必顯式指向內(nèi)容數(shù)組,但在本例中,該代碼指向 reverse
函數(shù),它以反序提供數(shù)據(jù)。 {{action}}
輔助程序讓用戶單擊每個(gè)錨標(biāo)記,并觸發(fā)指定的函數(shù)。 可能不太熟悉的唯一一個(gè)元素是 {{this}}
。 當(dāng)遍歷內(nèi)容數(shù)組時(shí),Ember 在 {{this}}
變量中保存對(duì)當(dāng)前索引的引用。 因?yàn)槊恳豁?xiàng)的值都只是一個(gè)字符串,您可以使用 {{this}}
直接輸出當(dāng)前項(xiàng)的值。 單擊 Twitter 用戶名,將再次加載該用戶的 tweet,而單擊這些 tweet 的名稱則將從 recentUsersController
刪除它們。
加載 tweet
保存搜索詞沒問題了,但實(shí)際執(zhí)行搜索又如何呢? 接下來,您將添加從 Twitter 檢索 JSON 包的片段,并將它顯示到頁面。 使用以下 Ember Model 并直接將它添加到標(biāo)簽為 Model 的注釋塊后面。 請(qǐng)記住,Ember Model 是它們將包含的數(shù)據(jù)是一個(gè)藍(lán)圖。
- App.Tweet = Em.Object.extend({ avatar: null, screen_name: null, text: null, date: null });
在 app.js 中,找到顯示為 App.recentUsersController.addUser(username);
的代碼行;并在它后面直接添加以下代碼:
- $.getJSON(url,function(data){ me.set('content', []); $(data).each(function(index,value){ var t = App.Tweet.create({ avatar: value.user.profile_image_url, screen_name: value.user.screen_name, text: value.text, date: value.created_at }); me.pushObject(t); }) });
如果您以前使用過 jQuery,您可能已經(jīng)使用過 .get()
函數(shù)來檢索數(shù)據(jù)。 .getJSON()
函數(shù)做同樣的事情,只是它將 JSON 包視作一個(gè)結(jié)果。 此外,它使用返回的 JSON 字符串,并將它轉(zhuǎn)換成可執(zhí)行的 JavaScript 代碼。 數(shù)據(jù)被檢索到之后,內(nèi)容數(shù)組被清空,刪除所有現(xiàn)有的 tweet。 下一行提取數(shù)據(jù)包,并將它封裝在一個(gè) jQuery 對(duì)象中,因此 .each()
方法可以循環(huán)所生成的 Tweets。 在 each
塊中,使用數(shù)據(jù)填充了 Tweet Model 的一個(gè)副本,然后將其推送到 ArrayController。
最后,您需要將以下顯示代碼添加到 index.html。直接復(fù)制并粘貼它到標(biāo)簽為 Tweets
的 h3
標(biāo)記后面。
- <ul> {{#each App.tweetsController}} <li> <img {{bindAttr src="avatar"}} /> <span>{{date}}</span> <h3>{{screen_name}}</h3> <p>{{text}}</p> </li> {{/each}} </ul>
Ember 使用純 {{Handlebars}}
可以很容易地輸出數(shù)據(jù)到頁面,但有一個(gè)問題。 還記得 Ember 如何在腳本標(biāo)記中包裝輸出的值嗎? 當(dāng)您使用 HTML 屬性時(shí),這并不是一個(gè)選項(xiàng)。 所以 Ember 提供 {{bindAttr}}
輔助程序。 在這個(gè)輔助程序內(nèi)放置的任何屬性都將如常輸出,但仍保留綁定。 繼續(xù)進(jìn)行操作,現(xiàn)在運(yùn)行您的應(yīng)用程序。 輸入一個(gè)用戶名,會(huì)看到 Tweets 出現(xiàn)。
下一步閱讀方向
在本文中,您學(xué)習(xí)了 Ember.js 功能的基礎(chǔ)知識(shí)。 您也學(xué)習(xí)了 Ember 如何使用其Models、Views、Controllers,當(dāng)然還有 Application 對(duì)象實(shí)施 MVC。 您使用 Handlebars 通過視圖輔助程序和操作輔助程序創(chuàng)建了模板。 您學(xué)習(xí)了如何使用 Models 創(chuàng)建數(shù)據(jù)的藍(lán)圖,使用 Controllers 將該數(shù)據(jù)存儲(chǔ)在集合集,并使用 Views 將數(shù)據(jù)顯示到頁面。 最后,您使用了 Ember 來構(gòu)建一個(gè)具有數(shù)據(jù)綁定、計(jì)算屬性和自動(dòng)更新模板的完整應(yīng)用程序。 您的母親將為您感到自豪!
原文鏈接:http://www.adobe.com/cn/devnet/html5/articles/flame-on-a-beginners-guide-to-emberjs.html