AngularJS - 使用ES6导入而不是角度DI系统

时间:2017-09-01 14:47:46

标签: angularjs typescript ecmascript-6

我使用带有角度1.6.x和Typescript的Webpack,我使用角度DI退出而支持ES6导入。当我需要某些ng函数时,例如$http$resource等我通过装饰器使用angular.injector函数直接注入它们,如下所示:

// inject.ts
    import * as angular from 'angular';

    export function inject (...params: string[]) {

        function doCall ( param: string, klass: Function) {
            const injector = angular.injector([ 'ng' ]);
            const service = injector.get(param);
            try {
                klass.prototype[ param ] = service;
            } catch ( e ) {
                window.console.warn( e );
            }
        }

        // tslint:disable-next-line:ban-types
        return function ( klass: Function ) {
            params.forEach( ( param ) => {
                doCall( param, klass );
            } );
        };
    }

// posts.service.ts
import { inject } from './inject';
import { IPost, Post } from './post';

@inject('$http')
export class PostsService {
    public $http: angular.IHttpService;
    get (): Promise<IPost[]> {
        const posts: IPost[] = [];
        const promise = new Promise<IPost[]>( (resolve, reject) => {
            this.$http.get<IPost[]>('https://jsonplaceholder.typicode.com/posts')
            .then(( response ) => {
                response.data.forEach(item => {
                    posts.push( new Post(item) );
                });

                resolve( posts );
            });
        });

        return promise;
    }
}


// post.ts
export interface IPost {
    userId: number;
    id: number;
    title: string;
    body: string;
}
export class Post implements IPost {
    userId: number;
    id: number;
    title: string;
    body: string;

    constructor (item: IPost) {
        this.userId = item.userId;
        this.id = item.id;
        this.title = item.title;
        this.body = item.body;
    }
}


// controller.ts
import { IPost } from './post';
import { PostsService } from './posts.service';

export class Controller {
    public postService: PostsService;
    public posts: IPost[];
    constructor ( private $scope: angular.IScope ) {
        this.postService = new PostsService();
    }

    $onInit () {
        this.postService.get()
        .then((posts) => {
            this.posts = posts;
            this.$scope.$digest();
        });
    }
}

// index.ts
import * as angular from 'angular';

import { Controller } from './app/controller';

import './index.scss';

export const app: string = 'app';

angular
  .module(app, [])
  .controller('controller', Controller);


angular.bootstrap(document.body, [app]);

我不知道它是否符合最佳做法,但它到目前为止工作得非常好。

我想听听你对这个问题的看法:使用这种方法是否有任何问题(表现,不良做法等)?

1 个答案:

答案 0 :(得分:3)

ES模块无法取代Angular模块和DI。它们相互补充,使应用程序保持模块化和可测试性。

ES6模块提供了额外的可扩展性层,例如控制器/服务子类化(对于Angular模块和单独的DI而言看起来不太好。)

使用ES6或TypeScript的推荐方法是按常规进行DI,$inject注释:

export class PostsService {
  static $inject = ['$http'];
  constructor(
    public $http: angular.IHttpService
  ) {}
  ...
}

每个文件都有一个模块也是一个好习惯,这样应用程序就可以保持模块化和可测试性:

export default angular.module('app.posts', [])
  .service('posts', `PostsService)
  .name;`

它的default导出是模块名称,可以在另一个直接依赖它的模块中导入:

import postsModule from '...';
...
export default angular.module('app.controller', [postsModule])
  .controller('controller', Controller)
  .name;`

通常无法从装饰器到达应用程序注入器。即使有可能通过黑客攻击使其在生产中运行,它也会在测试中搞砸。

angular.injector创建新的注入器(即应用程序实例),并且在生产中使用非常有限:

angular.injector(['ng']).get('$rootScope') !== angular.injector(['ng']).get('$rootScope');

当开发人员不知道如何获取当前$injector实例时,它经常被滥用。当然不应该在像这样的情况下使用它。