原文链接 : How to write AngularJS controller using TypeScript?
原文作者 : Siddharth Pandey
译者 : 李林璞 (web前端领域)
译者注:翻译如有疏漏,欢迎指出!感谢!
AngularJS 有许多强大的特性,其中之一便是 Controller。在这篇文章里,我将介绍如何使用 TypeScript 去编写 AngularJS 的 Controller。
Controller 通常用来增强 AngularJS 作用域(Scope)
。当一个 Controller 通过 ng-controller
指令连接到 DOM 上的时候,Angular 将使用指定的 Conroller 函数初始化一个新的 Controller 对象。一个新的子 scope 将被创建并作为 $scope
变量注入到 Controller 的构造函数当中。
有两个选项将 Controller 连接到视图当中,一种是 Controller 作为语法,另外一种是使用 $scope
。如果使用 Controller 语法,Controller 实例将被分配一个在新作用域上的属性。
要想知道类型定义,看看 这个令人吃惊的仓库 ,它收集了几乎所有流行的 JavaScript 库。这些类型定义可以让我们得到任何编译时错误和 IDE 的智能支持。我使用 Visual Studio 和 Visual Code,它们都对 TypeScript 有很好的支持。
正如上面提到的,AngularJS 只要在被请求的时候都将创建一个Controller 实例。所以,一个 Controller 可以使用 TypeScript 里的类去定义,就像我们所知道的,一个类是可以被实例化的。让我们来使用在视图里 Controller 作为语法的方法来定义一个 Dashboard Controller。下面的代码没有使用 $scope
服务。
interface IDashboardVm { news: { title: string, description: string }; messageCount: number; people: Array<any>; title: string; getMessageCount: () => ng.IPromise<number>; getPeople: () => ng.IPromise<Array<any>>; } class DashboardController implements IDashboardVm { static $inject: Array<string> = ['dataservice']; constructor(private dataservice: app.core.IDataService) { this.getMessageCount(); this.getPeople(); } news = { title: 'News', description: 'Internal server team is excited about AngularJS, TypeScript & JavaScript' }; messageCount: number = 0; people: Array<any> = []; hubsSummary: Array<any> = []; title: string = 'Dashboard'; getMessageCount() { return this.dataservice.getMessageCount().then((data) => { this.messageCount = data; return this.messageCount; }); } getPeople() { return this.dataservice.getPeople().then((data) => { this.people = data; return this.people; }); } } angular.module('app.dashboard').controller('DashboardController', DashboardController);
利用 TypeScript 的强类型特征,最好创建一个包含所有和视图相关成员和行为的接口。这就可以使为一个 Controller 定义实现变得容易,而且这个接口如果需要就可以做成一个抽象方法在其他地方使用了。所以,上面代码里我创建了一个名为 IDashboardVm
的接口。
接着,名为 DashboardController
的 Controller 实现了这个接口并给每个成员定义了默认状态。看这个类的静态变量 $inject
,它告诉了 AngularJS DI
在初始化这个 Controller 之前注入哪些依赖。然后构造器在需要的依赖的相同顺序定义了参数当它们被注入到那些参数的时候。
类所提到的依赖都是相当直接了当的,假设 dataservice
是一个自定义的 AngularJS 服务,它封装了所有对服务器发起的 HTTP 请求。根据接口里的每个定义,接下来我们要为这些行为定义实现,内部调用 dataservice
方法。它使用了 promises
去返回待会儿要分配到 Controller 成员上去控制状态的响应。
真正重要的是要注意使用 Angular 的模块 API 注册这个 Controller 的位置。上面的代码里,首先定义了类然后完成其注册。如果这个顺序交换的话,Angular 就将找不到我们这个 Controller 的实现了。当使用一个 JavaScript 构造函数就可以很好地解决问题,因为 函数提升起到了很重要的作用 。
下面是这个 Controller 如何在 Angular-UI UI-Router
中使用的代码片段,但如果你想使用 Angular 内置路由模块的话概念是一样的。注意,这只展示了使用 controllerAs
语法进行配置的部分。
config: { url: '/', templateUrl: 'app/dashboard/dashboard.html', controller: 'DashboardController', controllerAs: 'vm', title: 'dashboard', }
如果你想使用 $scope
服务的话,那么就可以像下面的代码片段那样扩展上面的接口。这将确保所有 IScope 有的成员可以通过接口访问到。使用这个方法还需要改变一下 Controller 类的实现,因为现在它需要 $scope
服务的依赖了。自定义接口类型接着就可以在构造器使用 $scope
参数获得强类型和智能支持了。
interface IDashboardVm extends angular.IScope { news: { title: string, description: string }; messageCount: number; people: Array<any>; title: string; getMessageCount: () => ng.IPromise<number>; getPeople: () => ng.IPromise<Array<any>>; }
如果你想学到更多有关如何整合 AngularJS 和 TypeScript 的知识,可以看看 我的 AngularJS 文章 。如果你想学习其他一些特别的东西可以联系我,我会尝试写相关文章的。