為什麼使用AngularJS 指令?
使用過 AngularJS 的朋友應該最感興趣的是它的指令。現今市場上的前端架構也只有AngularJS 擁有自訂指令的功能,並且AngularJS 是目前唯一提供Web應用可複用能力的架構。
目前有很多JavaScript 產品提供外掛程式給Web開發人員。例如, Bootstrap 就是當前比較流行的提供樣式和JavaScript外掛程式的前端開發套件。但是開發人員在使用Booostrap中的外掛程式時, 必須切換到JavaScript 模式來寫 jQuery 代碼來啟用外掛程式雖然jQuery 代碼寫起來十分簡單,但是必須和HTML進行同步,這是一個單調乏味且容易出錯的過程。
AngularJS首頁展示了一個簡單的例子,用於實現Bootstrap中的 Tab功能,可以在頁面中輕鬆添加 Tab 功能,並且使用方法和 ul 標籤一樣簡單。HTML代碼如下:
BootStrap Tab Component This is the content of the first tab. This is the content of the second tab.
JavaScript代碼如下:
angular.module('components', []). directive('tabs', function() { return { restrict: 'E', transclude: true, scope: {}, controller: [ "$scope", function($scope) { var panes = $scope.panes = []; $scope.select = function(pane) { angular.forEach(panes, function(pane) { pane.selected = false; }); pane.selected = true; } this.addPane = function(pane) { if (panes.length == 0) $scope.select(pane); panes.push(pane); } }], template: '' + '
' + '
- '+ '{{pane.title}}' + '
' + '
' + '' + '', replace: true }; }). directive('pane', function() { return { require: '^tabs', restrict: 'E', transclude: true, scope: { title: '@' }, link: function(scope, element, attrs, tabsCtrl) { tabsCtrl.addPane(scope); }, template: '' + '', replace: true }; })
你可以從以下連結查看效果:http://jsfiddle.net/powertoolsteam/GBE7N/1/
正如你所見,除了擁有用於實現指令的 和 標籤,頁面和常規HTML頁面沒有什麼區別。HTML開發人員無需編寫任何代碼。當然,總需要有第一個吃螃蟹的人,建立指令共用使用,但是目前Tabs指令已經很常見了,可以在任何地方複用(如BootStrap,、jQueryUI、Wijmo, 和一些知名的前端外掛程式集)。
由於指令的易用和易編寫,許多使用者已經開始使用AngularJS編寫指令了。例如, AngularJS 開發組已經基於AngularJS實現了一系列指令-UI Bootstrap 來代替Bootstrap; 知名ComponentOne 控制項廠商也在AngularJS 基礎上建立了Wijmo ;我們也可以在GitHub上找到一些公用指令資料庫:jQueryUI widgets。
擁有了 AngularJS,是不是覺得自己已經站在了巨人的肩膀上了?但是不要高興的太早,如果已經有了這麼多的指令供我們使用,那我們為什麼還要學習AngularJS ,為什麼還要學習自訂指令呢?
舉個簡單的例子,也許你有特殊的需求:假設你在一家財務公司工作,你需要建立一張財務表單,它需要以表格的形式展示資料、擁有綁定、編輯、校正並且同步資料更新到伺服器的功能。表單外掛程式很常見但是能夠滿足這些具體需求的不得而知了,所以你必鬚根據實際業務需求來建立自訂指令。
Offshore Investment Summary
這就是本篇文章的目的,接下來我們會討論如何建立 AngularJS指令。
建立自訂AngularJS 指令
文章開頭的自訂指令十分的簡單。它僅僅實現了同步的功能。一般指令是包含更多元素的:
//建立指令模組 (或者檢索現有模組) var m = angular.module("myApp");// 建立"my-dir"指令 myApp.directive("myDir", function() { return { restrict: "E", // 指令是一個元素 (並非屬性) scope: { // 設定指令對於的scope name: "@", // name 值傳遞 (字串,單向綁定) amount: "=", // amount 引用傳遞(雙向繫結) save: "&" // 儲存操作 }, template: // 替換HTML (使用scope中的變數) "" + " {{name}}: " + " Save" + "", replace: true, // 使用模板替換原始標記 transclude: false, // 不複製原始HTML內容 controller: [ "$scope", function ($scope) { … }], link: function (scope, element, attrs, controller) {…} } });
效果如下:
注意這個自訂指令遵循一種格式:以"my" 為首碼,類似於命名空間,因此如果你在應用中引用了多個模組指令,你可以通過首碼很容易的判斷出它是在哪定義的。這不是硬性要求,但是這樣做可以帶來很多便利。
指令的建構函式會返回帶有屬性的JavaScript 對象。這些內容在AngularJS 首頁中都有清晰說明。以下是我對一些屬性的理解:
restrict: 說明指令在HTML中的應用形式,備選項有"A"、"E" 和 "C", "M" ,分別代表 attribute、element、class和comment(預設值為"A")。我們將更多的關注attributes-如何建立UI元素。scope: 建立指令的作用範圍,scope在指令中作為屬性標籤傳遞。Scope 是建立可以複用指令的必要條件,每個指令(不論是處於嵌套指令的哪一級)都有其唯一的範圍,它不依賴於父scope。scope 對象定義names 和types 變數。上面的例子即建立了3個scope變數。name: "@" (值傳遞,單向綁定):
"@"符號表示變數是值傳遞。指令會檢索從父級scope中傳遞而來字串中的值。指令可以使用該值但無法修改,是最常用的變數。amount: "=" (引用,雙向繫結)
"="符號表示變數是引用傳遞。指令檢索主Scope中的引用取值。值可以是任意類型的,包括綜合物件和數組。指令可以更改父級Scope中的值,所以當指令需要修改父級Scope中的值時我們就需要使用這種類型。save: "&" (運算式)
“&”符號表示變數是在父級Scope中啟作用的運算式。它允許指令實現比修改值更進階的操作。 template: 替代原始模板中的標記的字串。替換功能將替換所有舊元素為新值。注意template是如何使用Scope中定義的變數的。這允許你無需寫任何額外的代碼即可建立macro-style 風格指令。replace: 說明是否替換原始標記中的值或是追加原始標記中的值。預設值是false,這時原始標記將被保留。transclude: 說明自訂指令是否複製原始標記中的內容。例如,之前展示的“tab”指令設定了transclude 為 true,因為tab 元素包含其他HTML 元素。 "dateInput" 指令則需要在初始化時為空白,所以需要設定transclude 為false。link: 該方法在指令中扮演著重要的角色。它負責執行DOM 操作和註冊事件監聽器等。link 方法包含以下參數:scope: 指令Scope的引用。scope 變數在初始化時是不被定義的,link 方法會註冊監視器監視值變化事件。element: 包含指令的DOM元素的引用, link 方法一般通過jQuery 操作執行個體(如果沒有載入jQuery,還可以使用Angular's jqLite )。controller: 在有嵌套指令的情況下使用。這個參數作用在於把子指令的引用提供給父指令,允許指令之間進行互動, tab 指令就是使用該參數較典型的例子:http://jsfiddle.net/powertoolsteam/GBE7N/1/
注意,當調用link 方法時, 通過值傳遞("@")的scope 變數將不會被初始化,它們將會在指令的生命週期中另一個時間點進行初始化,如果你需要監聽這個事件,可以使用scope.$watch 方法。
好了,以上即為自訂指令需要用到基本知識描述。如果你仍然不熟悉指令,最好的方法就是動手實現幾個小例子,可以在fiddle中進行實踐:http://jsfiddle.net/powertoolsteam/Tk92U/
在下一篇文章中我們將一起熟悉幾個AngularJS自訂指令。