原文連接 : How to write custom AngularJS Directive using TypeScript?
原文做者 : Siddharth Pandey
譯者 : 李林璞(web前端領域)
譯者注:翻譯若有疏漏,歡迎指出!感謝!轉載請保留此頭部。html
AngularJS 框架有不少強大的特性,其中指令(Directives)是廣爲人知的。在這篇文章中,我將告訴你如何用 TypeScript 編寫自定義 AngularJS 指令。首先,我將講一下關於指令的基本知識,但若是你想直接看 TypeScript 代碼的,你能夠跳過基本。前端
在較高層面上,指令是一個 DOM 元素的標記(像一個屬性,元素名稱,註釋或者 CSS 類),它告訴 Angular 的 HTML 編譯器(
$compiler
)去給 DOM 元素鏈接一個特殊的行爲(例如經過事件監聽),或者甚至是改變這個 DOM 元素和其子元素。gitAngular 自己有一些內建的指令,像
ngbind
,ngModel
和ngClass
。就像你建立控制器(controllers)和服務(services)那樣,你能夠建立本身的指令給 Angular 使用。當 Angular 啓動你的應用時,HTML 編譯器就會對 DOM 元素進行遍歷找到符合的指令。angularjs-AngualrJS 文檔github
看看ng-controller
和ng-bind
這些 AngularJS 框架自帶指令的使用方法:web
<div ng-controller="Controller"> Hello <input ng-model='name'> <hr/> <span data-ng-bind="name"></span> <br/> <span ng:bind="name"></span> <br/> <span ng_bind="name"></span> <br/> <span data-ng-bind="name"></span> <br/> <span x-ng-bind="name"></span> <br/> </div>
上面的代碼片斷,有多種方式去標記一個指令。AngularJS 的 HTML 編譯器負責決定哪一個元素匹配哪一個指令,通常經過區分大小寫的 camelCase
(駝峯式) 命名方法(如 ngModel
)去使用指令。可是,由於 HTML 是不區分大小寫的,通常咱們使用小寫形式的 短橫線-分隔 屬性寫在 DOM 元素上(如ng-model
)。typescript
標準化過程以下:bootstrap
前面的元素或屬性使用帶x-
或data-
的形式。segmentfault
將:
,-
或_
這些分隔符轉化爲camelCase
(駝峯式)。數組
最好使用 短橫線-分隔 格式(如 ng-bind
對應 ngBind
)。若是你想使用一個 HTML 驗證工具,可使用 帶data前綴 的版本進行替代(如 data-ng-bind
對應 ngBind
)。上面展現的其餘形式因遺留緣由仍然可使用當仍是建議避免使用它們。
AngularJS 的 HTML 編譯器如$compiler
能夠基於元素名,屬性,類名,還有註釋去匹配指令。下面的例子展現了一個名爲myDir
的指令在一個 HTML 模板裏是怎麼用多種方式去引用的:
<my-dir></my-dir> <span my-dir="exp"></span> <!-- directive: my-dir exp --> <span class="my-dir: exp;"></span>
最好經過標籤名或者屬性而不是註釋和類名去使用指令。這樣作一般會更容易去決定哪一個指令匹配給定的元素。
註釋指令一般在 DOM API 限制跨越多個元素建立指令的地方使用(如table
元素)。AngularJS 1.2 採用了ng-repeat-start
和ng-repeat-end
做爲解決這個問題更好的方法,鼓勵開發者儘可能使用這個方法代替註釋指令。
讓咱們來建立一個只爲任何的塊,小部件或者人名在右邊添加標題,子標題和文本的指令。這是一個很好的例子,由於它能夠在不少地方重用並且能夠做爲一個有隔離做用域的指令在每一個動態加載的塊中做爲信息展現。
來看看 HTML,Typescript 的代碼在其下方:
<div class="widget-head"> <div class="page-title pull-left">{{title}}</div> <small class="page-title-subtle" ng-show="subtitle">({{subtitle}})</small> <div class="widget-icons pull-right"></div> <small class="pull-right page-title-subtle" ng-show="rightText">{{rightText}}</small> <div class="clearfix"></div> </div> interface IHtWidgetHeaderScope { title: string; subtitle: string; rightText: string; allowCollapse: string; } //Usage: //<div ht-widget-header title="vm.map.title"></div> // Creates: // <div ht-widget-header="" // title="Movie" //</div> class HtWidgetHeader implements ng.IDirective { static $inject: Array<string> = ['']; constructor() { } static instance(): ng.IDirective { return new HtWidgetHeader(); } scope: IHtWidgetHeaderScope = { 'title': '@', 'subtitle': '@', 'rightText': '@' }; templateUrl: string = 'app/widgets/widget-header.html'; restrict: string = 'EA'; } angular.module('app').directive('htWidgetHeader', HtWidgetHeader.instance);
利用 TypeScript 的特色,建立一個定義了可在指令內使用的做用域成員的接口(interface)。一樣地咱們想建立一個指令的實例,咱們就定義一個實現了IDirective
的類(class)。
要想知道類型定義,看看這個使人吃驚的倉庫,它收集了幾乎全部流行的 JavaScript 庫。這些類型定義可讓咱們獲得任何編譯時錯誤和 IDE 的智能支持。我使用 Visual Studio 和 Visual Code,它們都對 TypeScript 有很好的支持。
這個指令不須要任何內建的 angular 服務或任何依賴,因此 $inject
這個靜態成員只是一個空數組。若是依賴被列出來的話,框架就會根據這個變量的內容去尋找而後依賴注入。
構造器(constructor)不用作什麼事情但咱們仍是須要一個靜態的 instance
方法去建立一個指令的實例。框架會在定義一個使用了模塊指令 API 的時候指望一個指令的實例。
這個類的 scope
在這裏很是重要,由於這個指令使用隔離的做用域,即它自身的成員變量能夠在這個指令的模板當中使用但並不繼承外層或其父級做用域的聲明。爲了可讀性和可維護性,咱們使用了 templateUrl
去指定模板的源碼。另外,restrict
設置了指令的使用級別給元素和屬性,分別使用 E 和 A 表示。
restrict
選項通常設置爲:
'A':只匹配屬性名
'E':只匹配元素名
'C':只匹配類名
'M':只匹配註釋
這些限制只要須要均可以結合:'AEC' 匹配屬性或元素或類名。
如今能夠像下面的代碼片斷那樣使用這個指令:
<div ht-widget-header title="{{vm.title}}" subtitle="{{vm.description}}" right-text="{{vm.refreshedDateTimeInfo}}"></div>
這個指令能夠在給到一個硬編碼或者動態的 title
,subtitle
或者 right-text
做用域成員的狀況下做爲元素或者屬性使用。注意到後者和任何指令同樣都已經被編譯器標準化。上面的代碼片斷在一個模板裏使用,該模板連接到一個含有 title
,description
和 refreshedDateTimeInfo
變量的控制器,而後展現給用戶。給定一些標記和設計,就會像下面這樣:
若是你想學到更多有關如何整合 AngularJS 和 TypeScript 的知識,能夠看看個人 AngularJS 文章。若是你想學習其餘一些特別的東西能夠聯繫我,我會嘗試寫相關文章的。