65.9K
CodeProject 正在变化。 阅读更多。
Home

AngularJS 应用程序中 TypeScript 入门

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.54/5 (16投票s)

2015年3月20日

CPOL

8分钟阅读

viewsIcon

148865

downloadIcon

4305

本文将帮助您开始在 AngularJS 应用程序中使用 TypeScript。

引言

AngularJS 是所有前端 Web 开发框架中当之无愧的冠军,好吧,我不是专家,因为老实说我并没有过多地使用过 Knockout、Backbone、AmberJS 等其他流行库,但最近几个月我一直在探索 AngularJS,并且我非常喜欢它。

AngularJS 带来了许多优点,它是一个设计精良、强大且经过深思熟虑的框架。在我看来,如果您是一名 Web 开发者,那么投入时间学习 AngularJS 是完全值得的,即使您不怎么从事前端工作,也不打算在不久的将来在您的项目中使用它。
 

背景

随着 ng-conf 上关于 AngularJS 和 TypeScript 协作的公告以及“Angular 2:基于 TypeScript 构建”的声明,开发者社区从 JavaScript 向 TypeScript 迁移以用于 AngularJS 项目的势头似乎非常强劲。我不是 TypeScript 的倡导者,也是 JavaScript 的忠实粉丝,但在这篇文章中,我将向您展示如何使用 TypeScript 开始使用 AngularJS,而避免对 TypeScript 和 JavaScript 进行任何比较。

本文结束时我们将实现什么?

我们将使用 TypeScript 构建一个非常简单的 AngularJS 应用程序。该应用程序将有一个视图,其中包含一个名为“获取我喜欢的曲目”的按钮。点击按钮后,它将显示一个曲目列表,很简单,不是吗?

我将使用 Visual Studio 2013 来创建演示应用程序,并且为了保持简洁,不会使用任何 REST API。

注意:虽然附带的解决方案是在 VS 2013 中创建的,但实际上并不需要。您可以下载附带的代码,然后将代码(当然要排除 .sln 和 .proj 文件)复制到解决方案中,它应该可以在任何 IDE 中正常工作,无需任何修改。

废话不多说,让我们开始吧。

创建项目结构

打开 Visual Studio,创建一个空的 Web 项目。删除 web.config 文件,我们不需要它。使用 nuget 包管理器添加以下引用,您也可以直接添加它们,nuget 是可选的。

  • AngularJS.Core
  • AngularJS.Route
  • angularjs.TypeScript.DefinitelyTyped(这是 AngularJS 的 TypeScript 定义)

可选地,您可以添加 Bootstrap 来美化您的应用程序。

在项目根目录中创建一个名为“app”的文件夹,我们将在其中添加应用程序的代码。

创建接口

在我们的应用程序中,我们将使用一个 AngularJS 服务,该服务将与后端 REST API 通信以获取曲目列表。我喜欢将所有接口定义与主要业务逻辑分开,所以让我们在“app”文件夹内创建另一个名为“interfaces”的文件夹,在该文件夹内创建一个空的 TypeScript 文件,并在其中添加以下代码。

module angularWithTS.Interfaces {

    export interface IPlaylistService {
        getPlayList: () => Array<ITrack>;
    }

    export interface ITrack {
        title: string;
        artist: string;
        rating: number;
    }
}

这里我们创建了一个名为“angularWithTS.Interfaces”的 TypeScript 模块。请注意,TypeScript 模块更像是其他编程语言中的命名空间,与 AngularJS 模块无关。但是,您稍后会看到我将 AngularJS 模块命名为“angularWithTS”,这只是我个人命名事物的一种风格,但您可以肯定地将其命名为任何您喜欢的名字。

您可以看到我在这个文件中添加了两个接口定义。接口“IPlaylistService”有一个名为“getPlayList”的方法,方法签名表明它不接受任何参数,并返回一个 ITrack 数组。

ITrack 只是同一文件中定义的另一个接口,它定义了我们的曲目对象的契约。使用接口可以确保我们应用程序中不会传递随机对象,并可以避免代码中意外的错误。这是 TypeScript 最突出的功能之一。

实现接口

您已经定义了服务接口“IPlaylistService”,现在让我们来实现它。在“app”文件夹内创建另一个文件夹并命名为“services”。然后添加一个名为“playlistService.ts”的新 TypeScript 文件,并在该文件中添加以下代码。

/// <reference path="../interfaces/interfaces.ts" />
module angularWithTS.Services {

    

    export class PlayListService implements angularWithTS.Interfaces.IPlaylistService {
        httpService: ng.IHttpService
        static $inject = ["$http"];
        constructor($http: ng.IHttpService) {
            this.httpService = $http;
        }

        getPlayList = () => {
            //For the purpose of this demo I am returning the hard coded values, hoever in real world application
            //You would probably use "this.httpService.get" method to call backend REST apis to fetch the data from server.
            
            var res: Array<angularWithTS.Interfaces.ITrack> = [
                { title: "Numb", artist: "Linkin Park", rating: 5 },
                { title: "Fire Flies", artist: "Owl City", rating: 4.3 },
                { title: "Yellow", artist: "coldplay", rating: 4.5 },
                { title: "Skyfall", artist: "Adele", rating: 4.5 }
            ];
            return res;
        }
    }
    angular.module("angularWithTS").service("angularWithTS.Services.PlayListService", PlayListService);
} 

如上代码所示,我们将服务封装在另一个名为“angularWithTS.Services”的命名空间中。“export”关键字确保我们的服务可以从“angularWithTS.Services”模块调用,而不仅仅是本地模块。

为了创建服务,我们创建一个新类,该类实现了“IPlaylistService”接口。在 AngularJS 应用程序中有 5 种创建服务的方法:service、factory、provider、value 和 constant,它们都有各自的用例和限制。AngularJS 将它们称为创建服务的 *recipe*,而 service 是我为本次演示选择的 recipe 名称。

我知道 service recipe 来创建服务真的令人困惑,但由于 AngularJS 团队在命名事物时没有咨询我们,我们将尝试与之共存。您可以在 AngularJS 开发人员指南中找到有关服务的更多信息。

我们的服务需要与后端 API 通信,因此需要 AngularJS 服务“$http”。我们可以在服务构造函数中指定此依赖项,Angular 将在运行时满足它。

请注意,函数的参数名为“$http”,其类型指定为“ng.IHttpService”。但是,为了让您的服务类能够通过像 Grunt 和 Gulp 这样的流行前端工具的最小化和压缩过程,您需要进行一个额外的步骤,如下所示。

static $inject = ["$http"];

$inject 是 AngularJS 框架将使用的特殊属性。请确保它被标记为“static”。它的值是一个字符串数组,每个字符串都是一个特定服务的标识符。此外,字符串在数组中的添加顺序很重要,此顺序应与构造函数参数的顺序相同。AngularJS 将在运行时检查此属性以确定需要注入哪些服务。

您还可以看到我们的服务确实实现了“IPlaylistService”接口中的“getPlayList”函数。但是,我们实际上并没有调用任何 REST API,而是返回硬编码的值,但我相信您已经明白了。

angular.module("angularWithTS").service("angularWithTS.Services.PlayListService", PlayListService);

上一行是标准的 AngularJS 代码,它将我们的类添加为 AngularJS 模块中的一个服务。我们还没有为我们的应用程序创建 Angular 模块,所以让我们在下一步创建一个。
 

创建 AngularJS 模块

要创建模块,请在“app”文件夹中添加“app.module.ts”文件。我喜欢这样命名模块文件,但同样,这只是个人偏好。在“app.module.ts”文件中添加以下代码:

((): void=> {
    var app = angular.module("angularWithTS", ['ngRoute']);
    app.config(angularWithTS.Routes.configureRoutes);
})() 

我们添加了 IIFE 并使用了 TypeScript lambda 函数来定义我们的代码。其中,第一行是创建了一个名为“angularWithTS”的 AngularJS 模块,它依赖于“ngRoute”模块。下一行是通过提供一个在“angularWithTS.Routes”类中定义的函数引用来配置我们的应用程序的路由信息,我们还没有创建该类,所以让我们快速创建一个。

定义路由

在“app”文件夹中再添加一个名为“app.routes.ts”的文件,并在其中添加以下代码:

/// <reference path="../scripts/typings/angularjs/angular.d.ts" />
/// <reference path="../scripts/typings/angularjs/angular-route.d.ts" />
module angularWithTS {
    export class Routes {
        static $inject = ["$routeProvider"];
        static configureRoutes($routeProvider: ng.route.IRouteProvider) {
            $routeProvider.when("/home", { controller: "angularWithTS.controllers.tsDemoController", templateUrl: "/app/views/playlist.html", controllerAs: "playList" });
            $routeProvider.otherwise({ redirectTo: "/home" });
        }
    }
}

上面的代码非常直接。但是,我想强调一点,即第一个注册路由的“controllerAs”参数。为“playList”提供的值表示,将绑定到该路由的控制器将通过“playList”别名在该路由的视图中访问。

这一点很重要,因为您可以借助此别名访问控制器的属性/方法,而无需依赖该控制器的“$scope”。我们将“angularWithTS.controllers.tsDemoController”控制器映射到我们的路由,我们将在下一步创建它。

创建控制器

让我们通过在“app”文件夹内添加一个名为“controllers”的新文件夹来创建控制器,然后添加一个名为“tsDemoController.ts”的新 TypeScript 文件,并添加以下代码。

/// <reference path="../services/playlistservice.ts" />
/// <reference path="../interfaces/interfaces.ts" />
/// <reference path="../../scripts/typings/angularjs/angular.d.ts" />
module angularWithTS.controllers {
    export class TSDemoController {

        playListService: angularWithTS.Interfaces.IPlaylistService;
        static $inject = ["angularWithTS.Services.PlayListService"];
        constructor(playListService: angularWithTS.Interfaces.IPlaylistService) {

            this.playListService = playListService;
        }
        favorites: Array<angularWithTS.Interfaces.ITrack>;

        getFavourites = () => {
            this.favorites = this.playListService.getPlayList();
        }
    }

    angular.module("angularWithTS").controller("angularWithTS.controllers.tsDemoController", TSDemoController);
} 

我们为控制器创建了一个名为“TSDemoController”的新类,并在构造函数中指定了它对“angularWithTS.Services.PlayListService”服务的依赖。再次注意“$inject”属性。

我们的控制器有一个名为“favorites”的属性,其类型为“Array<angularWithTS.Interfaces.ITrack>”。我们将使用此属性来绑定我们的视图。它还有一个名为“getFavourites”的函数,该函数只需使用服务调用的结果填充“favorites”属性。我们将在视图中将此函数用作点击事件处理程序。接下来,我们将创建视图。

创建视图

要将视图添加到我们的项目中,请在“app”文件夹中再添加一个名为“views”的文件夹,然后添加一个空的 HTML 文件“playlist.html”。在 HTML 文件中添加以下代码:

<div class="jumbotron">
    <ul class="list-group">
        <li ng-show="playList.favorites" class="list-group-item" ng-repeat="t in playList.favorites">
            {{t.title}}
        </li>
    </ul>
    <button class="btn btn-block" ng-click="playList.getFavourites()">Get Favorites Tracks</button>
</div>

如您所见,我们通过别名“playList.favorites”访问控制器属性“favorites”,而不是依赖“$scope”。其余部分不言自明。

整合

最后,我们几乎完成了应用程序。让我们在“app”中添加一个外壳“Index.html”文件,如下所示。请确保您已将所有第三方脚本和 CSS 文件添加到项目中,这些文件在 Index.html 中有引用。

<!DOCTYPE html>
<html ng-app="angularWithTS">
<head>
    <title>AngularJS With TypeScript</title>
    <link href="content/css/bootstrap.min.css" rel="stylesheet" />
    <link href="content/css/bootstrap.custom.min.css" rel="stylesheet" />
</head>
<body>
    <div class="container">
        <div class="page-header">Click the following button to get your favorite tracks.</div>
        <div class="row">
            <div class="col-md-4 col-md-offset-4">
                <div ng-view="">

                </div>
            </div>
        </div>
    </div>
    <script src="scripts/angular.js"></script>
    <script src="scripts/angular-route.js"></script>
    <script src="app/app.routes.js"></script>
    <script src="app/app.module.js"></script>
    <script src="app/services/playlistService.js"></script>
    <script src="app/controllers/tsDemoController.js"></script>
</body>
</html>

 

结论

在这篇文章中,我只是触及了皮毛。当然,一篇文章无法充分展示 AngularJS 或 TypeScript。希望它能帮助您开始在 AngularJS 项目中使用 TypeScript。

 

© . All rights reserved.