前言 - 碁峰資訊epaper.gotop.com.tw/PDFSample/A403.pdf · • 第3...

18
前言 筆者記得第一次接觸 AngularJS 時,它被稱為 Angular,這是由筆者的同事 Misko 工程 師利用業餘時間所建立的一個開源函式庫(open source library)。在當時,筆者所屬團 隊已經花了數個月,以一個高效率與可維護的方式奮力開發 Google Feedback(這是當 時團隊負責開發的專案)。我們寫了超過 18,000 行的程式碼,其中很多都是未經測試, 而且對於無力繼續快速增加程式功能而深感沮喪。Misko Hevery,這位筆者剛才提到的 工程師,大膽的表示他可以把我們過去花六個月開發的所有內容,只用兩週的時間就可 以完全實現出來。筆者在此聲明,當時我們成員都是 Java 工程師,完全缺乏 JavaScript 的認知。 我們當下抱持的態度是:接下來兩週預期會看到 Misko 掙扎、混亂與失誤之後而面臨失 敗的結果。但一個星期過後,他已經複製出我們花了六個月所開發的內容。曾經是一個 18,000 行程式碼庫(codebase)已經下降到只有 1500 行,而且幾乎每個單一功能片段都 是模組化(modular)、可重用以及可測試的。我們認為 Misko 似乎秘密的進行某些工作! 本書的另一位作者 Brad Green,在當時看到令人驚奇的事情不斷出現,而且與 Misko 定建立一個團隊專注於實現重要的核心理念:讓建置 Web 應用程式變得簡單。筆者負責 Google Faceback 成為採用 AngularJS 開發的第一個專案,而且 AngularJS 真正幫助我 們從 JavaScript 框架的 Web 開發人員角度去理解其中重要的內容。 AngularJS 是如何從一個分支專案開始迅速起飛到成為 Web 上有領先地位的 JavaScript 框架呢(筆者稱它為超框架(meta-framework))?有很多很棒的原因,而且有個樂於 助人的開發人員和貢獻者所組成的超級 AngularJS 社群是最大的主因,其中在最近的 AngularJS 版本中有納入來自此開源社群的所有功能。數以千計的開發人員每天依靠 AngularJS 過活,每月還有超過數千人開始使用 AngularJS。而每個開發人員透過自己的 使用經驗讓 AngularJS 變得更好。

Transcript of 前言 - 碁峰資訊epaper.gotop.com.tw/PDFSample/A403.pdf · • 第3...

Page 1: 前言 - 碁峰資訊epaper.gotop.com.tw/PDFSample/A403.pdf · • 第3 章:單元測試,探討的內容是使用Karma 與Jasmine 對AngularJS 專案進行單元 測試。 •

前言

筆者記得第一次接觸 AngularJS時,它被稱為 Angular,這是由筆者的同事 Misko工程師利用業餘時間所建立的一個開源函式庫(open source library)。在當時,筆者所屬團隊已經花了數個月,以一個高效率與可維護的方式奮力開發 Google Feedback(這是當時團隊負責開發的專案)。我們寫了超過 18,000行的程式碼,其中很多都是未經測試,而且對於無力繼續快速增加程式功能而深感沮喪。Misko Hevery,這位筆者剛才提到的工程師,大膽的表示他可以把我們過去花六個月開發的所有內容,只用兩週的時間就可

以完全實現出來。筆者在此聲明,當時我們成員都是 Java工程師,完全缺乏 JavaScript的認知。

我們當下抱持的態度是:接下來兩週預期會看到 Misko掙扎、混亂與失誤之後而面臨失敗的結果。但一個星期過後,他已經複製出我們花了六個月所開發的內容。曾經是一個

18,000行程式碼庫(codebase)已經下降到只有 1500行,而且幾乎每個單一功能片段都是模組化(modular)、可重用以及可測試的。我們認為Misko似乎秘密的進行某些工作!

本書的另一位作者 Brad Green,在當時看到令人驚奇的事情不斷出現,而且與 Misko決定建立一個團隊專注於實現重要的核心理念:讓建置Web應用程式變得簡單。筆者負責的 Google Faceback成為採用 AngularJS開發的第一個專案,而且 AngularJS真正幫助我們從 JavaScript框架的Web開發人員角度去理解其中重要的內容。

AngularJS是如何從一個分支專案開始迅速起飛到成為 Web上有領先地位的 JavaScript框架呢(筆者稱它為超框架(meta-framework))?有很多很棒的原因,而且有個樂於助人的開發人員和貢獻者所組成的超級 AngularJS社群是最大的主因,其中在最近的AngularJS版本中有納入來自此開源社群的所有功能。數以千計的開發人員每天依靠AngularJS過活,每月還有超過數千人開始使用 AngularJS。而每個開發人員透過自己的使用經驗讓 AngularJS變得更好。

Page 2: 前言 - 碁峰資訊epaper.gotop.com.tw/PDFSample/A403.pdf · • 第3 章:單元測試,探討的內容是使用Karma 與Jasmine 對AngularJS 專案進行單元 測試。 •

iv | 前言

我們興奮的獻出本書,並盼望可以從本書讀者的經驗中學習。

誰適合閱讀本書

本書適合想要以 AngularJS入門的新手,無論是在一個分支專案、附加工具或項目主體上開始使用 AngularJS 都適合閱讀本書。筆者預期,讀者在開始閱讀本書之前,對於 JavaScript 的使用印象還不錯,而且對這個程式語言具有基本認知應足以學習AngularJS。本書的內容從 AngularJS入門到進階概念諸如 directives(指令)都會涵蓋討論。我們會對 AngularJS做逐步的探討,所以請放輕鬆並且跟我們一起快樂的學習。

本書目的

當筆者寫了第一本關於 AngularJS的書,當時並沒有學習這個軟體框架的簡單方法,它的文件說明混淆不清(而且現在在一定程度上仍然是如此)。本書目的是針對 AngularJS的入門提出逐步的指引,AngularJS的內容是分層安排,有一些非常簡單而強大的概念,以及一些進階而難以獲取的功能。筆者希望以一個有組織的分階方式,一點一滴的

逐步增加複雜度而帶著讀者去了解這些內容的每一個部分。

讀完本書之後,讀者應該能夠迅速的對 AngularJS專案上手,並且能夠了解如何開發大型可維護與高效能的應用程式。

Web應用程式開發現況JavaScript從只是一個 scripting語言(或親切地稱為 hack)而僅用來做次要的驗證到如今成為一個成熟的程式語言,這過程是漫漫長路。jQuery做了很多基礎工作在確保瀏覽器的相容性,並且提供堅固穩定的 API,讓所有瀏覽器可以順利運作並且與網頁的DOM互動。隨著應用程式複雜度和規模的增長,jQuery這種 DOM的操作階層,變得不足以自行提供堅固、模組化、可測試以及容易了解的框架用於開發應用程式,其中每

個 jQuery專案的內容看起來完全異於另一個專案的內容。

AngularJS(與相當多的其他 JavaScript MVC框架)解決這個問題的方法是:在 jQuery之上以及 DOM之上提供一個階層去思考應用程式結構和可維護性方面的項目,而減少我們最終要撰寫的範本程式碼數量。關於以一致的方式使用框架的最好的部分是:一個新的開

發人員進來時對於專案的結構、外觀佈局以及開發會立即有所感。我們期望一個框架的定

Page 3: 前言 - 碁峰資訊epaper.gotop.com.tw/PDFSample/A403.pdf · • 第3 章:單元測試,探討的內容是使用Karma 與Jasmine 對AngularJS 專案進行單元 測試。 •

前言 | v

位是:開發人員可以把時間專注在程式的外觀和感覺以及其核心功能的開發,而不必在意

範本程式與冗餘的程式碼。

目前在Web應用程式開發的中心思維,而且也是屬於 AngularJS核心的一些概念是:

• 資料驅動(Data-driven)程式設計,目的是為了操控 model(模型),以及讓框架做繁重的工作和 UI描繪。

• 宣告性(Declarative)程式設計,其在執行動作時必須宣告意圖;而不是命令式(imperative)程式設計,其在一個單獨的檔案或函式(function)執行實際工作,而非在需要效果之處動作。

• 模組性(Modularity)和關注點分離(separation of concerns),它們主要將應用程式分成較小的、可重用的功能片段,各自負責單一的事項。

• 可測試性,這樣就可以確保身為開發人員撰寫實際上要做的預期內容。

• 其他。

在 AngularJS框架的幫助下,我們可以用可管理與可維護的方式專注於開發極度複雜的新時代Web應用程式。

本書內容簡介

本書目的是帶著開發人員逐步了解 AngularJS的每個單元,其中在介紹一個新概念單元的每一章之後,會立即接著一章的篇幅討論此相關概念項目的單元測試(unit test)。以下是本書的內容簡介:

• 第 1章:AngularJS 初探,簡介 AngularJS以及它背後的概念。這一章也會開始撰寫一個 AngularJS應用程式。

• 第 2章:基本 Directives 與 Controllers,開始介紹一些 AngularJS內建的 directives,以及 controllers(控制器)的概念。

• 第 3章:單元測試,探討的內容是使用 Karma與 Jasmine對 AngularJS專案進行單元 測試。

• 第 4章:表單與輸入,討論表單主題以及 AngularJS與表單的最佳搭配運用內容。

• 第 5章:Services 總論,介紹 AngularJS services的概念,一些常見的 AngularJS內建 services,以及建立自訂的 services。

• 第 6章:$http 伺服器通訊,涉及使用 AngularJS的 $http與伺服器作通訊,以及介紹進階的 $http概念,諸如:攔截器(interceptors)與轉換器(transformers)。

Page 4: 前言 - 碁峰資訊epaper.gotop.com.tw/PDFSample/A403.pdf · • 第3 章:單元測試,探討的內容是使用Karma 與Jasmine 對AngularJS 專案進行單元 測試。 •

vi | 前言

• 第 7 章:Services 與 XHRs 的單元測試,接著探討 services 的單元測試並且使用$httpBackend模擬(mocking)伺服器的請求(requests)。

• 第 8章:Filters介紹 AngularJS filters;而第 9章:Filters 的單元測試,說明 filters的單元測試。

• 第 10 章:ngRoute 路由,討論 SPA(單一頁面應用程式)裡使用選擇性的模組ngRoute所作的路由。

• 第 11章:Directives,介紹 directives一些基本概念以及建立 directives。

• 第 12章:Directives 的單元測試,討論 directives的單元測試。

• 第 13章:進階 Directives,牽涉進階的 directive建置概念,諸如:compile(編譯)、transclusion(嵌入)、controllers以及 require。這一章還提供第三方 widget整合成一個 directive的範例。

• 第 14章:端對端測試,說明使用 Protractor與 WebDriver進行 AngularJS應用程式的端對端測試。

• 第 15章:AngularJS 準則與最佳慣例,將前面章節所述的內容集結成最佳慣例、指

引以及有用的工具。

筆者將整個程式碼儲存庫(repository)放在 GitHub,所以如果讀者不想要敲擊鍵盤輸入本書的程式碼範例,或者要確保正在尋找最新的程式碼範例,那就瀏覽這個儲存庫

(repository)來取得相關的內容(http://bit.ly/1pNdTQg)。

如果讀者像我們一樣,不會從頭到尾徹底閱讀一本書。如果讀者真的像我們一樣,平

時一點也不會閱讀簡介內容。萬一讀者剛好看到目前這一段,以下我們提供一些選讀 建議:

• 如果讀者之前已經用過 AngularJS,則可以跳過第 1章。

• 第 2章深入探討 ng-repeat以及可以運用的各種方法與 ng-repeat最佳化。

• 第 3 章、第 7 章、第 9 章和第 12 章分別討論 controllers、services、filters 以及directives的單元測試,所以如果讀者想了解更多關於這些項目的單元測試,可以直接跳到這些章節閱讀。

• 如果對於使用 Protractor作端對端測試感興趣,可以跳到第 14章閱讀。

• 如果讀者想了解 dirctives,並充分利用 directives提供的強大功能,那不能錯過第 11章和第 13章。

• 如果讀者想要明白一個運用到路由、授權等等項目的全面性 AngularJS應用程式,可以研究第 10章的最後一個範例。

Page 5: 前言 - 碁峰資訊epaper.gotop.com.tw/PDFSample/A403.pdf · • 第3 章:單元測試,探討的內容是使用Karma 與Jasmine 對AngularJS 專案進行單元 測試。 •

第一章

AngularJS初探

網際網路(internet)從誕生以來已有長足的進展。非互動式的消費性網站開始朝向提供使用者互動的性質發展。使用者能夠在這些網站上作回應(respond),填寫詳細資料,處理所有的電子郵件。平行使用(concurrent usage)、離線支援(offline support)以及其他不少的項目皆成為網路的基本功能,而且用戶端應用程式(client-side application)的規模與範疇也不斷地增長與茁壯。

由於應用程式變得更大、更好以及更快,因而增加開發人員需要介入管理的複雜度

(complexity)。一套純 JavaScript/jQuery解決方案,並非都具有確保軟體快速開發或長期維護的正確結構,專案開發因而高度依賴優質的軟體工程師來建立初始的軟體框架

(framework)。即便如此,模組性(modularity)、可測性(testability)以及關注點分離(separation of concerns)的需求可能無法納入專案之中。在這樣的情況之下,測試的執行和可靠度(reliability)的確認往往被視為專案中次要的工作。

AngularJS(http://www.angularjs.org)是為了滿足這樣的基本需求而誕生。是否能夠提

供一個標準的結構與超框架(meta-framework)以便可靠又快速的開發出Web應用程式呢?諸如可測試的程式碼、關注點分離以及 MVC(Model-View-Controller,或者更確切的稱為:MVVM)等等,這些軟體工程概念可以套用於 JavaScript應用程式嗎?能否找到兩全其美的方式─兼備 JavaScript的簡潔特性以及快速可維護的開發需要呢?筆者認為 AngularJS是可行的框架,但還是希望讀者透過本書的內容對於 AngularJS有所認識之後,來作個公平的裁判。

本章末尾會建置一個 AngularJS 版的「Hello World」基本範例,藉由它來描述AngularJS背後一些常見的概念和理念。另外也會說明如何啟動(bootstrap)AngularJS以及將 HTML 內容換成 AngularJS 應用程式,還會討論 AngularJS 應用的資料綁定(data-binding)技術。

Page 6: 前言 - 碁峰資訊epaper.gotop.com.tw/PDFSample/A403.pdf · • 第3 章:單元測試,探討的內容是使用Karma 與Jasmine 對AngularJS 專案進行單元 測試。 •

2 | 第一章

AngularJS初探AngularJS 是 Web 領域中強力的 JavaScript MVC 框架。之所以稱為強力的原因是AngularJS替開發人員做了不少工作,因此我們只需要專注於自己的核心應用程式,至於其他事情則交由 AngularJS處理。所以傳統用於伺服端(server side)上可信賴的標準軟體工程實務,也可應用於用戶端(client-side)程式設計中,進而加速前端的程式開發。AngularJS提供一個可擴展的一致性結構,使得軟體團隊在開發複雜的大型應用程式變得輕而易舉。

而最好的部分是什麼呢?一切的開發都在純 JavaScript程式與 HTML內容中完成,不需要學習另一種新的程式設計或模板(templating)語言(然而讀者必須了解 MVC或MVVM方式的應用程式開發,而本書會簡介這些內容)。至於 AngularJS是如何實現這些看似瘋狂與奇妙而感覺不可能滿足的承諾呢?

AngularJS的理念是由架構應用程式、將程式碼掛勾(hooked)在一塊、對程式進行測試以及將程式與其他函式庫(libraries)整合,這些項目所驅動的內容而生成的一些關鍵原則來推動。在進入上述每個項目的討論之前,先來了解我們應該在乎的事項。

何謂 MVC(Model-View-Controller)?AngularJS框架背後的核心概念是 MVC架構範式(pattern)。Model-View-Controller範式(或 MVVM,即 Model-View-ViewModel的簡稱,它與 MVC相當類似)是在開發大型應用程式時,將邏輯單元與關注點分離的一種發展方式。這讓開發人員在決定劃分工

作的方式與位置有個出發點。 MVC的架構範式將應用程式分為三個不同的單元:

• model(模型)是應用程式的驅動力。一般指的是應用程式背後的資料,內容通常是

從伺服器(server)取得,使用者在 UI(使用者介面)中所見的任何資料內容來自於 model或 model的子集。

• view(檢視)是使用者能夠看到並與之互動的 UI,它是根據應用程式當前的 model而生成的動態內容。

• controller(控制器)是工作邏輯(business logic)呈現層,其執行的工作諸如:取得資料,以及決定如何針對 model來呈現可顯示的部分內容等等。

因為 controller基本上負責決定 model的某些部分要顯示在 view裡,所以根據實作,也可以把 controller視為是 viewmodel(檢視模型)或 presenter(呈現器)。

Page 7: 前言 - 碁峰資訊epaper.gotop.com.tw/PDFSample/A403.pdf · • 第3 章:單元測試,探討的內容是使用Karma 與Jasmine 對AngularJS 專案進行單元 測試。 •

AngularJS初探 | 3

範式的核心是將應用程式內的工作分成個別的子單元(subunits),而這樣的做法具有以下的好處:

• 每個單元只負責一件工作,其中 model是資料,view是 UI,而 controller是工作邏輯。因為單一工作的原則,可以輕而易舉的找出正在運作的新程式碼以及尋找先前

的程式碼所屬位置。

• 每個單元盡可能與其他單元獨立分開,進而讓程式碼更加模組化、可重用(reusable)以及易於維護。

AngularJS的好處本節會描述 AngularJS的一些好處,而接下來的小節,會針對這些好處做相關內容的延伸探討:

• AngularJS是個單一頁面應用程式(Single Page Application, SPA)的超框架。由於用戶端模板和 JavaScript的高度使用,建立與維護應用程式可能會變得繁瑣與沉重。AngularJS則會移除冗餘的程式碼並執行繁重的工作,使得我們能夠完全專注於應用程式的核心內容。

• 比起使用 jQuery這個純 JavaScript的解決方案,AngularJS應用程式只需要較少的程式碼就可完成相同的工作(task)。和其他的框架作比較時,讀者依然會發現自己能寫出較少的範本(boilerplate)以及更簡潔的程式碼,因為 AngularJS會將工作邏輯從 view移轉到可重用的元件中。

• 在 AngularJS應用程式裡撰寫的大多數程式碼,會聚焦於工作邏輯或者核心應用程式的功能,而非例行的冗餘程式碼。我們不需要像平常一樣撰寫所有的範本,因為 AngularJS會處理它們以及負責實行 MVC架構範式。

• AngularJS的宣告性質(declarative nature)讓人更容易撰寫與理解應用程式,開發人員很容易只藉由觀看 HTML內容和 controller來了解應用程式的意圖。以某種意義上來說,AngularJS可以建立適合開發人員所需求的 HTML子集─ HTMLX(而不是依賴 HTML5或期待 HTML6等等)。

• AngularJS 應用程式可以使用與工作邏輯和功能無關的 CSS 和 HTML 來做樣式(styled)。也就是說,不需要動用到任何一行 JavaScript程式碼,它就可以完全改變應用程式的整個外觀佈局與設計。

• AngularJS應用程式模板(templates)是以純 HTML來撰寫,所以設計師會發現搭配它們的作業以及為它們做樣式都很容易。

Page 8: 前言 - 碁峰資訊epaper.gotop.com.tw/PDFSample/A403.pdf · • 第3 章:單元測試,探討的內容是使用Karma 與Jasmine 對AngularJS 專案進行單元 測試。 •

4 | 第一章

• 替 AngularJS應用程式進行單元測試(unit test)可說是非常簡單,這也使得應用程式的穩定性與易於維護性可以維持一段較長的時間。想要增加新的功能嗎?需要更

改現有的邏輯嗎?伴隨著堅若磐石的測試基底,這一切都是輕而易舉的工作。

• 並不需要放棄那些我們喜愛的 jQueryUI或 BootStrap元件,其中 AngularJS恰當地扮演著第三方元件庫(third-party component library),並提供掛鉤(hooks)以便使用我們認定的方式來作相關的程式整合。

AngularJS理念AngularJS支持六個核心理念,這些理念讓開發人員可以輕易快速的建立複雜龐大的應用程式:

資料驅動(透過資料綁定(data-binding))在傳統的伺服端應用程式中,會利用 HTML內容與本機資料(local data)結合來建立使用者介面。當然,這意味著:只想要改變 UI的部分內容時,伺服器必須再次送出整個 HTML內容與資料到用戶端,即便用戶端已經具有大部分的 HTML內容也要如此重複動作。

採行用戶端單一頁面應用程式(SPAs)會有個優勢,只需要從伺服器把變更的資料發送到用戶那裡,而用戶端程式碼按照新的資料來更新 UI。結果可能看起來像下列的範本(假設使用 jQuery),首先是某個非常簡單的 HTML:

Hello <span id="name"></span>

接著是一段可以執行的 JavaScript程式:

var updateNameInUI = function(name) { $('#name').text(name);};

//這裡可能放一些程式碼⋯//載入初始資料updateNameInUI(user.name);

//接著以某種方式改變資料updateNameInUI(updatedName);

上述的程式碼定義一個 updateNameInUI函式(function),將使用者名稱帶入函式,然後會找到 HTML裡對應此名稱的 UI元素(element),並更新此元素區塊的innerText(內部文字)。當然,我們必須確定呼叫該函式時的 name值變化,譬如初始載入時,以及可能在使用者登出後又重新登入時,或者在使用者修改自己的名

稱時。而目前這個範例只是一個欄位的運作情況。現在想像一下,在整個程式中有

Page 9: 前言 - 碁峰資訊epaper.gotop.com.tw/PDFSample/A403.pdf · • 第3 章:單元測試,探討的內容是使用Karma 與Jasmine 對AngularJS 專案進行單元 測試。 •

AngularJS初探 | 5

幾十行類似的程式碼在運作。在一個像這樣 CRUD(Create-Retrieve-Update-Delete(建立─取出─更新─刪除))的 model裡有這些類型的運作是很常見的。

至於 AngularJS的作法則是用 model來驅動,其中會使用到資料綁定這個 AngularJS的核心功能─ AngularJS儲存千行的範本程式碼來實現核心功能。在 AngularJS應用程式中,不必把時間浪費在 UI與 JavaScript之間的資料來回存取,只是將資料綁定到 HTML裡,然後 AngularJS就會留意取得的資料值有無放入 UI。不僅如此,而且它也會在每當資料發生變化時,處理 UI的相關內容更新。

與上述範例具有完全相同功能的 AngularJS應用程式,則如下所述:

Hello <span>{{name}}</span>

此時 JavaScript程式裡需要做的是設定變數 name的值。 AngularJS會負責確認它的內容已經改變,然後自動更新 UI對應的呈現。

這是單向(one-way)的資料綁定,在此取得來自於伺服器(或任何其他來源)的資料,並更新 DOM(Document Object Model)。但是,如果方向反過來會怎樣呢?表單(forms)的傳統處理方式─需要從使用者那邊取得資料,執行一些內容處理,然後

將處理過的內容發送給伺服器─整個流程如下所述,第一個 HTML內容可能是:

<form name="myForm" onsubmit="submitData()"> <input type="text" id="nameField"/> <input type="text" id="emailField"/></form>

而對應可執行的 JavaScript程式可能是:

//設定表單(form)裡的內容function setUserDetails(userDetails) { $('#nameField').value(userDetails.name); $('#emailField').value(userDetails.email);}

function getUserDetails() { return { name: $('#nameField').value(), email: $('#emailField').value() };}

var submitData = function() { //假設有個產生 XHR請求(request)的函式 //產生含有 JSON資料的請求 makeXhrRequest('http://my/url', getUserDetails());};

Page 10: 前言 - 碁峰資訊epaper.gotop.com.tw/PDFSample/A403.pdf · • 第3 章:單元測試,探討的內容是使用Karma 與Jasmine 對AngularJS 專案進行單元 測試。 •

6 | 第一章

除了外觀佈局和模板之外,我們必須對工作邏輯以及 UI或後端的控制程式碼之間的資料流加以管理。隨時有資料改變的話,需要更新 UI內容,而每當使用者提交(submits)內容或需要執行內容驗證時,則需要呼叫 getUserDetails()函式,然後對資料內容做確實的核心邏輯處理。

AngularJS 提供雙向(two-way)資料綁定,也讓我們得以跳脫上述範本程式碼的撰寫。雙向資料綁定確保 controller 和 UI 共享相同的 model,而從一方(UI或 controller 程式碼)更新內容時,則另一方的更新工作會自動處理。所以,用AngularJS實作上述範例的相同功能時,HTML的內容可能會是:

<form name="myForm" ng-submit="ctrl.submitData()"> <input type="text" ng-model="user.name"/> <input type="text" ng-model="user.email"/></form>

在 HTML中的每個輸入標籤(input tag),會綁定到 ng-model屬性(attribute)所宣告的 model中(ng-model是一個 AngularJS directive(指令))。當提交表單時,AngularJS藉由觸發 controller中的 submitData函式來作掛勾。對應相關的 JavaScript程式則是:

//controller程式碼內部this.submitData = function() { //產生含有 JSON物件(object)的伺服器 POST請求 $http.post('http://my/url', this.user);};

AngularJS使用雙向資料綁定,程式會從 UI獲取最新的內容值,並自動地更新 user物件(object)裡的 name和 email,它也確保 user物件裡的 name和 email內容值所做的任何更改,會自動反映到 DOM裡。

採用 AngularJS的資料綁定來設計應用程式,我們可以專注於自己的核心工作邏輯和功能,而讓 AngularJS負責更新 UI的重擔。這也意味著對於開發 AngularJS應用程式來說,開發人員需要作設計心態的轉變。需要更新 UI嗎?那就改變 model,而讓 AngularJS來更新 UI內容。

宣告性

單一頁面 Web應用程式(也稱為 AJAX應用程式)是透過 JavaScrip將多個單獨的HTML內容片段和資料縫合在一起而構成。而大多數的時候,最終會得到無軌跡可尋的非固定內容呈現的 HTML模板。例如,考慮下列的 HTML內容:

Page 11: 前言 - 碁峰資訊epaper.gotop.com.tw/PDFSample/A403.pdf · • 第3 章:單元測試,探討的內容是使用Karma 與Jasmine 對AngularJS 專案進行單元 測試。 •

AngularJS初探 | 7

<ul class="nav nav-tabs"> <li>Home</li> <li class="selected">Profile</li></ul>

<div class="tab1"> Some content here</div><div class="tab2"> <input id="startDate" type="text"/></div>

此刻,如果讀者使用過某些 HTML結構或是熟悉 jQuery或其類似框架的話,也許能夠推測上述的 HTML將呈現出一組分頁(tabs),而在第二個分頁有個會轉變成日期選擇器(datepicker)類型的輸入欄位。但這些都不是實際在 HTML中描述的內容,只是因為程式碼庫(codebase )裡有一些 JS和 CSS,它們的工作是將這些 li元素換成分頁以及將 input欄位換成日期選擇器(datepicker)。

基本上這是命令式(imperative paradigm)的程式設計,必須告訴應用程式究竟要

如何做到每一個動作,請它找到具有 navtabs類別的元素,並將它作為一個分頁元件,然後預設選擇第一個分頁。這件事完全是在 JavaScript程式碼中完成的,而不是在實際的 HTML中需要改變內容的所在之處作業,HTML並無呈現任何的相關 邏輯。

AngularJS反而推動的是宣告式(declarative paradigm)的程式設計,它會在嘗試完

成動作的 HTML裡宣告正確的內容。主要透過 AngularJS裡一些 directives(指令)

的項目來達成,directives基本上用來延伸 HTML的詞彙,讓它擁有新的能力。讓AngularJS弄清楚如何完成我們想要它做的事情:無論是在建立分頁或是日期選擇器(datepickers)。所以上述的範例程式改用 AngularJS來撰寫的內容如下:

<tabs> <tab title="Home">Some content here</tab> <tab title="Profile"> <input type="text" datepicker ng-model="startDate"/> </tab></tabs>

AngularJS式的 HTML使用 <tab>標籤,告訴 AngularJS弄清楚如何呈現分頁元件,並宣告 <input>是一個 datepicker,而且將它綁定到 startDate model變數中。

Page 12: 前言 - 碁峰資訊epaper.gotop.com.tw/PDFSample/A403.pdf · • 第3 章:單元測試,探討的內容是使用Karma 與Jasmine 對AngularJS 專案進行單元 測試。 •

8 | 第一章

這種做法有下列幾個優點:

• 這是宣告性質的,所以只要觀看 HTML內容,可以立刻指出它有兩個分頁,而其中一個分頁裡面含有日期選擇器(datepicker)欄位。

• 選擇當前的分頁,取消選擇其他的分頁,以及隱藏和顯式正確內容等等的工作邏輯,全都被封裝在此 tab directive裡面。

• 同樣的,運用日期選擇器(datepicker)的任何開發人員不必知道它的內部實作使用的是 jQueryUI、BootStrap、或其他技術。已經把實作與使用分離,因而有明確的關注點分離。

• 因為將整個功能封裝與涵蓋在一個位置,所以可以在一個集中的位置做內容的改變,並且由它來影響使用到此相關內容的所有地方,而不是手動的找尋與替換每

個 API呼叫的位置。

分離關注點

AngularJS 採用類 Model-View-Controller(MVC-like)範式來構建任何的應用程式。如果我們考慮使用 AngularJS的話,應用程式會有三個部分要處理。

首先是要顯示給使用者知道或使用者透過應用程式輸入的實際資料。這是 AngularJS專案裡的 model,其中大部分是純資料,並使用 JSON物件來呈現。

接著是使用者介面或是使用者要觀看與互動而最終描繪的 HTML內容,它會顯示資料給使用者,這就是 view。

最後是實際工作邏輯與程式碼,諸如:取得資料、決定呈現什麼 model內容給使用者、處理驗證的方式等等─應用程式特定的核心邏輯,這是 AngularJS應用程式的 controller。

我們認為 MVC或類 MVC是不錯的方法,其中有幾個堅持的理由:

• 應用程式內部彼此之間有清楚的關注點分離。需要一些工作邏輯嗎?那就使用controller。需要不同的方式呈現嗎?那可以利用 view。

• 團隊成員與合作者對於了解我們的程式而言,因為有一組結構與範式而有立即明瞭的勝算。

• 因為種種原因需要重新設計 UI嗎?無需更改任何 JavaScript程式碼就能做到。需要改變內容驗證的方式嗎?無需變動 HTML內容即可達成。它可以對程式碼庫的部分內容獨立作業,而不會影響到其他的部分。

• AngularJS 不算是完全的 MVC,其中 controller 永遠不會直接引用(direct reference)到 view。這樣算是不錯的設計,因為可以讓 controller與 view彼此獨立,同時也可以不需實體化(instantiate)一個 DOM就能輕鬆地測試 controller。

Page 13: 前言 - 碁峰資訊epaper.gotop.com.tw/PDFSample/A403.pdf · • 第3 章:單元測試,探討的內容是使用Karma 與Jasmine 對AngularJS 專案進行單元 測試。 •

AngularJS初探 | 9

基於上述的理由,MVC本著容易維護、擴充與測試的方式來開發和擴展應用程式。

相依性注入(Dependency Injection)AngularJS是少數內建成熟相依性注入系統的 JavaScript框架的其中之一。相依性注入(於第 5章會討論)是索取特定 controller或 service(服務)的相依性內容之概念,而非使用 new運算子在內部對它們做實體化或明顯的呼叫函式來生成實體(例如:DatabaseFactory.getInstance())。程式碼的其他部分轉為負責(即:注入器

(injector))確定如何建立這些相依性內容,並在需要時提供出來。

這樣的作法是有益的,因為:

• 索取相依性內容的 controller、service或函式不需要知道構建此相依性內容方式,即能夠深入串聯與依賴自如。

• 這個做法明確,所以在開始處理某段程式碼之前,可以馬上就知道我們需要 什麼。

• 它讓測試變得超級簡單,因為我們可以用比較好的模擬(mocks)來取代相依性內容以便執行測試。所以不須使用 HttpService與真實的伺服器作通訊,只要利用 MockHttpService與建立在記憶體的伺服器作溝通。

AngularJS 的相依性注入可用於所有的單元,從 controller 與 service 到模組(modules)與測試(tests)都可使用,它可以讓我們輕鬆地撰寫模組化與可重用的程式碼,這樣就能夠在需要的時候清楚又簡單的套用。

擴展(Extensible)上一節在討論 AngularJS的宣告性質時,已經提到 directives(指令)。directives是AngularJS教導瀏覽器和 HTML擁有新能力的方式,從處理點擊(clicks)和狀況(conditionals),到建立新的結構和樣式皆可包辦。

但是,這只是一套內建的 directives,此外 AngularJS 有開放它內部用來建立這些 directives 的相同 API,讓任何人都可以擴展現有的 directives 或建立自訂的directives。我們可以整合諸如 jQueryUI與 BootStrap的第三方函式庫(third-party libraries)來開發出功能強大的 directives,例如:用這些 directives來建立一個專門針對特殊需求的語言。第 11章會建立自訂的 directives。

預設情況是 AngularJS有一大組的核心 directives供我們入門使用,並且提供 API讓我們實現 AngularJS可以完成的所有事情,甚至做得比原來還多還好的功能。所以我們的創造力真的可以只侷限在建立宣告性與可重用的元件上面。

Page 14: 前言 - 碁峰資訊epaper.gotop.com.tw/PDFSample/A403.pdf · • 第3 章:單元測試,探討的內容是使用Karma 與Jasmine 對AngularJS 專案進行單元 測試。 •

10 | 第一章

先測試、再測試、持續測試

我們前面所提到的許多好處實際上是源於 AngularJS具備測試和可測性的非凡特點。AngularJS的每個小單元都被設計成可測試的,從它的 controllers、services與directives到它的 views與 routes(路由)皆是如此。

在相依性注入與不引用 view的 controller之間,可以很容易測試 AngularJS應用程式裡的 JS程式碼。因為我們可以取得用於產品程式裡的同一個相依性注入系統用在測試程式裡,就很容易對任何的 service做實體化,而不必在意它的依賴性內容。而這一切都是使用功能完美且運作快速的測試執行器(test runner)Karma(http://

karma-runner.github.io/)來執行。

當然,為了確保應用程式端對端(end-to-end)確實的運作,我們也會使用Protractor(https://github.com/angular/protractor),它是一個專為 AngularJS 設計的WebDriver式端對端情境執行器(end-to-end scenario runner)。這表示我們不必在端對端測試程式寫任何隨機等待與觀察的內容,例如:等待一個元素顯示,或在一次

的點擊之後,等待伺服器的回應而停留五秒鐘。Protractor能夠掛勾到 AngularJS並指明何時進行測試,提供一套堅固又穩定的端對端測試。

我們準備在第 3章使用 Karma,並且會討論相關設定,至於 Protractor則留在第 14章探討。因此假若我們沒有完整的施行 AngularJS應用程式的測試,實在沒有寬容的理由。大家趕快行動吧,相信讀者和所屬團隊的成員會因此而感謝自己的作為。

對於 AngularJS的優美,本書已經做過簡單的概述,接著開始撰寫自己的 AngularJS應用程式。

開始撰寫 AngularJS程式開始撰寫一個 AngularJS應用程式是前所未有的簡單作業,但在進入這個主題之前,先花點時間來討論幾個簡單的問題,進而協助讀者評估 AngularJS是否屬於適合採用的軟體開發框架。

需要什麼後端(backend)?我們通常會問的第一個問題是:撰寫 AngularJS應用程式所需的相關後端為何?在此很簡短的作個回答:並沒有這樣的要求。

Page 15: 前言 - 碁峰資訊epaper.gotop.com.tw/PDFSample/A403.pdf · • 第3 章:單元測試,探討的內容是使用Karma 與Jasmine 對AngularJS 專案進行單元 測試。 •

AngularJS初探 | 11

以單一頁面應用程式運作所需的後端而言,AngularJS並沒有做特別的規定,其中可以自由使用 Java、Python、Ruby、C#或者自己喜愛的的任何其他程式語言實作。需要處理的唯一事情就是反覆與伺服器通訊的方式。理想情況下,會透過 XHR(XML HTTP requests)或 sockets來處理。

假如伺服器具有可提供 JSON內容值的 REST或 API端點時,則後端開發的難易程度就等同前端介面的開發,甚至比前端更容易一些。但是,即便伺服器不能處理 JSON內容,並不表示應該放棄採用 AngularJS方案,因為我們可以很容易的指導 AngularJS與XML伺服器通訊或以任何其他的形式做溝通。

整個應用程式都需要變成 AngularJS App嗎?答案是不需要。AngularJS有一個 ng-app的概念(技術上來說,這是一個 directive,下一節會作相關的探討),它可以註釋任何具有標籤的 HTML元素(而不是只針對 <html>或 <body>標籤元素),其中可要求 AngularJS只針對 HTML特定的區塊內容作動作、控制以及調整。

因而可以非常簡單的從現有應用程式的一個小區塊開始交由 AngularJS操控,然後才逐漸擴大 AngularJS的控制區域。

基本的 AngularJS應用程式討論完上述的內容,最後,我們把討論焦點擺在一些程式碼上面。先從最基本的

AngularJS應用程式開始,這例子只是匯入 AngularJS函式庫,然後啟動 AngularJS來正常運作:

<!-- File: chapter1/basic-angularjs-app.html --><!DOCTYPE html><html ng-app>

<body> <h1>Hello {{1 + 2}}</h1>

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.19/angular.js"></script></body>

</html>

Page 16: 前言 - 碁峰資訊epaper.gotop.com.tw/PDFSample/A403.pdf · • 第3 章:單元測試,探討的內容是使用Karma 與Jasmine 對AngularJS 專案進行單元 測試。 •

12 | 第一章

關於本書的範例

本書的所有範例都放在 GitHub 儲存庫(https://github.com/shyamseshadri/

angularjs-up-and-running)。假如讀者執行書上的範例而遇到任何問題時,

可以來這個位置找到最新版和正確版的程式再作嘗試。書中每個範例也

都會以註解方式在開頭列出路徑檔名,如此一來可以在 GitHub儲存庫

(repository)相關位置找到對應的範例內容。每個章節都有自己的目錄

所在位置,這樣對應找到書中的範例說明會更容易一點。

開啟一個 AngularJS應用程式分為下列兩個步驟:

載入 AngularJS原始碼這裡引用的版本是 Google Hosted Libraries的未壓縮(unminified) 的版本,當然讀者也可以引入自己本機維護的版本。Google CDN(https://developers.google.com/

speed/libraries/devguide)裡面有 AngularJS的所有最新版本,讀者可以直接從此處選擇引入,或者直接從 AngularJS網站下載(http://www.angularjs.org)。

啟動 AngularJS(Bootstrapping AngularJS)這步驟是透過 ng-app來完成,它是 AngularJS第一個也是最重要的 directive,用來表示由 AngularJS控制的 HTML內容區塊。把它放到 <html>標籤中通知 AngularJS要控制整個 HTML應用程式,也可以把它放在 <body>或頁面上任何的其他元素中。而被標示的元素內部包含的任何元素都會被 AngularJS以相同 directive效果來處理,被標示的元素之外的內容則不會受到此 directives的影響。

最後,範例有個 AngularJS單向資料綁定,這是本書對於資料綁定的首次示範,其中我們把「1+2」表示式(expressions)放在雙大掛號中,雙大括號是 AngularJS的專用語法,用來表示單向資料綁定或 AngularJS表示式。如果它囊括的是一個變數,則 UI的內容會隨著變數值的改變而更新,如果它裡面是一個表示式,則 AngularJS會做相應的運算,而只要運算的結果有變化,對應的 UI內容就會是最新的值。

假如某種原因讓 AngularJS沒有正確地被啟動,我們會在 UI中看到 {{1 + 2}}的文字,但如果啟動無誤,則應該在瀏覽器上看到如圖 1-1中的畫面內容。

Page 17: 前言 - 碁峰資訊epaper.gotop.com.tw/PDFSample/A403.pdf · • 第3 章:單元測試,探討的內容是使用Karma 與Jasmine 對AngularJS 專案進行單元 測試。 •

AngularJS初探 | 13

圖 1-1 基本的 AngularJS應用程式範例

AngularJS版本的 hello world此時,已經了解 AngularJS應用程式的建立流程,接著就來建立傳統的「hello world」應用程式。針對這個例子,會有一個輸入欄位讓使用者輸入他們的名字。然後,在使用

者輸入時,程式會根據輸入欄位裡的最新內容來更新 UI的呈現。聽起來很複雜嗎?接下來就來討論相關內容:

<!-- File: chapter1/angularjs-hello-world.html --><!DOCTYPE html><html>

<body ng-app> <input type="text" ng-model="name" placeholder="Enter your name"> <h1>Hello <span ng-bind="name"></span></h1><script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.19/angular.js"></script></body>

</html>

在此針對上一個範例額外新增兩個事項,而原來的兩個事項維持不變:

• AngularJS的原始碼仍然是一樣的,這裡將 ng-app移到 <body>標籤中。

• 新增一個 input標籤,裡面還加一個 ng-model。ng-model directive 用於輸入欄位中,希望使用者輸入任何資料時,JavaScript可以處理對應的內容值。在此,要求AngularJS將使用者在這個欄位輸入的內容值,儲存於 name變數中。

• 另外還新增一個 ng-bind,使用這個 directive的位置可以改用雙大括號來取代,所以可以把 <span ng-bind="name"></span>換成 {{ name }},兩者都會完成相同的工作,就是把變數 name的值放到對應的標籤中,並在值有變化時,隨著更新 UI內容。

Page 18: 前言 - 碁峰資訊epaper.gotop.com.tw/PDFSample/A403.pdf · • 第3 章:單元測試,探討的內容是使用Karma 與Jasmine 對AngularJS 專案進行單元 測試。 •

14 | 第一章

這個範例的執行結果如圖 1-2。

圖 1-2 AngularJS的「hello world」範例

本章總結

本章討論兩個非常簡單的 AngularJS應用程式。首先建立一個非常簡單的 AngularJS應用程式,而第二個範例展現 AngularJS的雙向資料綁定的能力。最棒的是我們不需要撰寫任何的 JavaScript程式碼就能完成這些範例。用純 JavaScript撰寫同樣的應用程式會需要建立監聽器(listeners)與 jQuery DOM的操控器(manipulators),使用 AngularJS則可以免除這一切麻煩瑣事。

我們也探討到 AngularJS的基本理念以及一些好處,還有它與現有解決方案的不同之處。下一章要來認識 AngularJS的一些最常見的單元內容,諸如:常見的 directives、controllers的運作以及 services的使用。

第二章

基本 Directives與 Controllers

第 1章已經建立一個非常簡單與容易的 AngularJS應用程式,也列舉 AngularJS版本的基本「hello world」範例,而本章將要延伸這個範例來作後續的探討。

我們會探索 AngularJS的模組(modules)與 controllers,並且建立自訂的 controllers。然後使用這些 controllers載入資料或在應用程式中作描述,以及操控 HTML去執行常見的工作,諸如:在 UI顯現項目陣列(array)、有條件的隱藏與顯示元素、在某些情況下對 HTML元素做樣式(styling)等等。

AngularJS模組首先要介紹的是模組(modules)的概念。模組是 AngularJS包裝相關程式碼的形式,並以單一名稱來表達這包程式碼。對於有使用 Java經驗的人來說,AngularJS的模組就像是 Java的 packages(套件)。

AngularJS模組含有兩個部分:

• 模組可以定義自己的 controllers、services、factories和 directives,這些都是可以透過模組存取的函式和程式碼。

• 模組也可以依賴其他模組來做為相依性內容(dependencies),這些相依性內容會在

此模組被實體化後隨著被定義出來。換句話說,AngularJS會去尋找特定名稱的相依性內容模組,並確保定義在相依性內容模組裡所有的函式、controllers、services等等項目可以供給此模組內的所有程式碼加以利用。