原文连接 : 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 文章。若是你想学习其余一些特别的东西能够联系我,我会尝试写相关文章的。