用Typescript如火如荼地进行angular.js

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/mzl87/article/details/85345132

目录

介绍

设置环境

将指令翻译成惯用的Typescript

翻译服务

翻译值提供者

翻译控制器

使tsconfig.json更严格

离开angular.js边界

结论


本文介绍如何迁移旧的angular.js项目以使用typescript

介绍

Typescript开始越来越受欢迎,因为静态类型提供了它的好处。尽管如此,一些参与使用angular.js支持项目的开发人员可能会因缺乏社区提供使用angular.jstypescript的说明而陷入困境。本文将尽力填补这一空白。

我们的战略涉及在每个开发阶段输出工作产品。因此,在现实生活中,可以逐步过渡到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

猜你喜欢

转载自blog.csdn.net/mzl87/article/details/85345132