目录
介绍
设置环境
将指令翻译成惯用的Typescript
翻译服务
翻译值提供者
翻译控制器
使tsconfig.json更严格
离开angular.js边界
结论
本文介绍如何迁移旧的angular.js项目以使用typescript
介绍Typescript开始越来越受欢迎,因为静态类型提供了它的好处。尽管如此,一些参与使用angular.js支持项目的开发人员可能会因缺乏社区提供使用angular.js和typescript的说明而陷入困境。本文将尽力填补这一空白。
我们的战略涉及在每个开发阶段输出工作产品。因此,在现实生活中,可以逐步过渡到typescript,从而不会损害开发团队必须达到的业务目标。
这篇文章将包含一些参考代码片段,但是如果你想更深入地学习这个主题,我建议你跟随github 项目,这是我已经翻译成typescript的现有项目的一个分支。
设置环境首先,我们需要安装以下依赖项:
- typescript
- gulp-typescript——为了执行各自的gulp任务
- @types/angular将为angular.js内部添加强类型
接下来,我们在项目的根目录中创建tsconfig.json,如下所示:
{
"compilerOptions": {
"allowJs": true,
"module": "none",
"target": "es5",
"types": [
"angular"
]
},
"include": [
"./src/**/*.ts"
]
}
我们将模块系统指定为' none',因为我们将解决模块依赖关系的工作留给angular.js,而不是像webpack这样的模块解析器。
另外,请注意我们指定类型的部分类型,例如@types/angular。
目标es5允许我们不要创建涉及babel.js的苛刻的转换piplines。
现在让我们在现有文件中添加一个gulp任务:
var ts = require('gulp-typescript');
var tsProject = ts.createProject("tsconfig.json");
//Compile all typescript files into javascript
gulp.task('ts-build', function() {
return gulp.src(['src/**/*.ts'])
.pipe(tsProject())
.pipe(gulp.dest("src/"));
});
现在我们可以为现有的任务调用我们的任务:
gulp.task('usemin', ['inject-templates', 'ts-build'], function() {
现在我们已经建立了自己的环境并准备好了。此外,一切仍然正常,并准备发货。
将指令翻译成惯用的Typescript策略是从自治单位开始翻译,并依靠已经翻译的项目继续其他单位,这样您就可以获得静态类型的好处。您还可以在任意点指定您的所有非翻译依赖类型作为转换,但在我看来,这会削弱强类型的好处,并且我建议从指令和服务开始,这些指令和服务作为您的angur.js应用程序的基础。
对于指令,您可以将.js扩展名重命名为.ts,但仍然可以利用angular.js类型和您在下面的指令中定义的类型系统:
class NgEnterDirective implements ng.IDirective {
public link = (scope : any, element : JQLite, attrs : ng.IAttributes) => {
element.bind("keydown keypress", (event) => {
if(event.which === 13) {
scope.$apply(function(){
scope.$eval(attrs.ngEnter);
});
event.preventDefault();
}
});
}
public static Factory(): ng.IDirectiveFactory {
return () => new NgEnterDirective();
}
}
angular
.module('app.core')
.directive('ngEnter', NgEnterDirective.Factory());
翻译服务
让我们来看看在我们的案例研究应用程序中的ShowService:
class Actor {
name: string
character: string
}
class Show {
id: number
original_name: string
cast: Actor[]
genres: string[]
}
class TvServiceResponse {
results: Show[]
}
/*
* Contains a service to communicate with the TRACK TV API
*/
class ShowService {
static $inject = ["$http", "$log", "moment"]
constructor(private $http : ng.IHttpService,
private $log : ng.ILogService,
private moment : any) {
return this;
}
private API_KEY : string = '87de9079e74c828116acce677f6f255b'
private BASE_URL : string = 'http://api.themoviedb.org/3'
private makeRequest = (url : string, params : any) : any => {
let requestUrl = `${this.BASE_URL}/${url}?api_key=${this.API_KEY}`;
angular.forEach(params, function(value, key){
requestUrl = `${requestUrl}&${key}=${value}`;
});
return this.$http({
'url': requestUrl,
'method': 'GET',
'headers': {
'Content-Type': 'application/json'
},
'cache': true
}).then((response) => {
return response.data;
}).catch(this.dataServiceError);
}
getPremieres = () => {
//Get first day of the current month
let date = new Date();
date.setDate(1);
return this.makeRequest('discover/tv',
{'first_air_date.gte': this.moment(date), append_to_response: 'genres'}).then
((data : TvServiceResponse) => {
return data.results;
});
}
get = (id : number) => {
return this.makeRequest(`tv/${id}`, {});
}
getCast = (id : number) => {
return this.makeRequest(`tv/${id}/credits`, {});
}
search = (query : string) => {
return this.makeRequest('search/tv', {query: query}).then((data : TvServiceResponse) => {
return data.results;
});
}
getPopular = () => {
return this.makeRequest('tv/popular', {}).then((data : TvServiceResponse) => {
return data.results;
});
}
private dataServiceError = (errorResponse : string) => {
this.$log.error('XHR Failed for ShowService');
this.$log.error(errorResponse);
return errorResponse;
}
}
angular
.module('app.services')
.factory('ShowService', ShowService);
在这一点上,不仅值得一提的是我们如何使用我们的DTO来确保我们的程序正常工作,还有我们如何利用ES6功能,如箭头函数或字符串插值。
这里的诀窍是,我们在tsconfig.json中指定的typescript会转换为ES5 。
翻译值提供者另一个自治部分的翻译看起来很简单:
class PageValues {
title : string
description : string
loading : boolean
}
angular
.module('app.core')
.value('PageValues', PageValues);
翻译控制器
在转换的这个阶段,我们可以将强类型依赖项注入控制器并进行转换。
这是一个例子:
class SearchController {
query: string;
shows: any[];
loading: boolean;
setSearch = () => {
const query = encodeURI(this.query);
this.$location.path(`/search/${query}`);
}
performSearch = (query : string) => {
this.loading = true;
this.ShowService.search(query).then((response : Show[]) => {
this.shows = response;
this.loading = false;
});
};
constructor(private $location : ng.ILocationService,
private $routeParams: any,
private ShowService: ShowService) {
PageValues.instance.title = "SEARCH";
PageValues.instance.description = "Search for your favorite TV shows.";
this.query = '';
this.shows = [];
this.loading = false;
if (typeof $routeParams.query != "undefined") {
this.performSearch($routeParams.query);
this.query = decodeURI($routeParams.query);
}
}
}
'use strict';
angular
.module('app.core')
.controller('SearchController', SearchController);
使tsconfig.json更严格
此时,当我们在应用程序中获得typescript时,我们可以使我们的tsconfig.json更严格。这样,我们可以应用更多级别的代码正确性检查。
让我们来看看我们可以添加的一些有用的选项:
{
"compilerOptions": {
"allowJs": true,
"alwaysStrict": true,
"module": "none",
"noImplicitAny": true,
"noImplicitThis": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"target": "es5",
"types": [
"angular"
]
},
"include": [
"./src/**/*.ts"
]
}
离开angular.js边界
值得一提的另一件事是使用typescript允许我们在不依赖angular.js结构的情况下构建应用程序的逻辑。如果我们需要构建一些业务逻辑,否则将受到angular.js约束的限制,这可能很有用,也就是说,我们希望采用动态多态,但是构建在angular.js依赖注入中,而不是限制而不是授权我们。
对于我们的玩具示例,让我们回到值提供者,这很简单但又可以为您提供一些关于如何不受限于angular.js结构的整体印象。
class PageValues {
title : string
description : string
loading : boolean
static instance : PageValues = new PageValues();
}
注意我们现在如何使用单例模式与静态实例,并摆脱angular.js模块连线。
现在我们可以通过以下方式从angular.js应用程序的任何部分调用它:
PageValues.instance.title = "VIEW";
PageValues.instance.description = `Overview, seasons & info for '${show.original_name}'.`;
结论
前端社区被认为是变化最快的社区。这可能会导致应用程序的客户端不断用顶级的自定义框架重写,以便开发人员团队仍然可以享受获得前端社区支持的好处。然而,并非每个开发团队,特别是大型企业,由于需要追逐业务目标而能够负担得起这样的奢侈品。
我的文章应该为这些团队提供一些帮助,以便连接到一些现代社区解决方案,而不会在很大程度上牺牲他们的业务目标。
我文章的最新部分显示的另一个值得注意的事情是,如果您想为前端应用程序架构添加一些灵活性,那么您可以轻松地摆脱框架的观点。
原文地址:https://www.codeproject.com/Articles/1272739/Cooking-angular-js-with-Typescript