web-dev-qa-db-ja.com

グローバルスコープを汚染しないTypeScriptクラスを使用して、AngularJSサービスを定義するにはどうすればよいですか?

私はAngularJSとTypeScriptを使用しています。次のように、TypeScriptクラスを使用してAngularJSサービスを実装します。

class HelloService {
    public getWelcomeMessage():String {
        return "Hello";
    }
}

angular.module('app.services.helloService', []).factory('helloService', () => {
    return new HelloService();
});

これは、次のjavascriptコードにコンパイルされます。

var HelloService = (function () {
    function HelloService() {
    }
    HelloService.prototype.getWelcomeMessage = function () {
        return "Hello";
    };
    return HelloService;
})();

angular.module('app.services.helloService', []).factory('helloService', function () {
    return new HelloService();
});

これは、グローバルな名前空間を変数HelloServiceで汚染しますが、これは明らかに望ましくありません。 (Chromeのコンソールを使用して、HelloServiceがオブジェクトであることを確認しました。)この問題を解決/回避するにはどうすればよいですか?

私は明白なことを試しました:

angular.module('app.services.helloService', []).factory('helloService', function () {
    class HelloService { ...} 
    return new HelloService();
});

しかし、それは私にコンパイルエラーを与えます(「予期しないトークン。「ステートメント」が必要です。))。

私が考えることができる1つの可能な解決策は、TypeScriptのインポートとエクスポートを何らかの方法で使用することです。これは、RequireJSを使用します。これはおそらく、定義関数内でHelloServiceをラップし、HelloServiceによるグローバルスコープの汚染を回避します。ただし、AngularJSは私の使用には十分であり、複雑さを増すため、AngularJSアプリケーションでRequireJSを使用したくありません。

だから、私の質問は、グローバルスコープを汚染しないTypeScriptクラスを使用してAngularJSサービスをどのように定義できますか?

35

私が実際にやったことを提供する必要があります:

module MyModule {
    export class HelloService {
        public getWelcomeMessage():String {
            return "Hello";
        }
    }

    angular.module('app.services.helloService', []).factory('helloService', () => {
        return new HelloService();
    });
}

このように私は使用できます

return new HelloService();

の代わりに

return new MyModule.HelloService();
14

2016-05-06:ES6スタイルのモジュールを使用した新しい例

静的な$inject配列とコンストラクターは、前の例から変更されていません。

唯一の変更は、クラスを複数のファイルに分割し、ES6モジュールを使用してクラス定義をプルすることです。

/ lib/HelloService.ts:

export class HelloService {
    public getWelcomeMessage():String {
        return "Hello from HelloService";
    }
}

/ lib/AnotherService.ts:

import {HelloService} from './HelloService';

/**
 * Service that depends on HelloService.
 */
export class AnotherService {

    // Define `HelloService` as a dependency.
    static $inject = ['HelloService'];
    constructor(
        // Add the parameter and type definition.
        public HelloService: HelloService
    ){}

    public getWelcomeMessage():String {
        // Access the service as: `this.HelloService`
        // Enjoy auto-completion and type safety :)
        var helloMsg = this.HelloService.getWelcomeMessage();
        return "Welcome from AnotherService, " + helloMsg;
    }
}

/ index.ts:

// Using the services.
import {HelloService} from './lib/HelloService';
import {AnotherService} from './lib/AnotherService';

angular.module('HelloApp', [])
    .service('HelloService', HelloService)
    .service('AnotherService', AnotherService)
    .run(['AnotherService', function(AnotherService: AnotherService){
        console.log(AnotherService.getWelcomeMessage());
    }]);

前の答え:名前空間を使用する

からの構築Steve Fenton's answer

依存性注入を許可するには、クラスに静的$inject配列を追加します。

$inject配列がどのように機能するかについては、 Angular $ injector documentation を参照してください。

依存関係は、配列によって指定された順序でコンストラクターに注入されます(そして、縮小化で動作するようになります)。

依存性注入の例:

namespace MyModule {
    /**
     * Angular Service
     */
    export class HelloService {
        public getWelcomeMessage():String {
            return "Hello from HelloService";
        }
    }

    /**
     * Service that depends on HelloService.
     */
    export class AnotherService {

        // Define `HelloService` as a dependency.
        static $inject = ['HelloService'];
        constructor(
            // Add the parameter and type definition.
            public HelloService: MyModule.HelloService
        ){}

        public getWelcomeMessage():String {
            // Access the service as: `this.HelloService`
            // Enjoy auto-completion and type safety :)
            var helloMsg = this.HelloService.getWelcomeMessage();
            return "Welcome from AnotherService, " + helloMsg;
        }
    }
}


// Using the services.
angular.module('app.services.helloService', [])
    .service('HelloService', MyModule.HelloService)
    .service('AnotherService', MyModule.AnotherService)
    .run(['AnotherService', function(AnotherService: MyModule.AnotherService){
        console.log(AnotherService.getWelcomeMessage());
    }]);
34
Sly_cardinal

私には2つの解決策があります。1つ目はクラスベースの構文を提供し、2つ目はグローバルスコープにまったく何も残しません...

グローバルスコープに1つのハンドルを追加するだけで、わずかに妥協することができます(これは、現在1つのクラスしかないため、グローバルスコープに配置したくない複数のクラスがある場合に当てはまります)。

次のコードは、グローバルスコープ内のモジュールのみを残します。

module MyModule {
    export class HelloService {
        public getWelcomeMessage():String {
            return "Hello";
        }
    }

    export class AnotherService {
        public getWelcomeMessage():String {
            return "Hello";
        }
    }

}

angular.module('app.services.helloService', []).factory('helloService', () => {
    return new MyModule.HelloService();
});

angular.module('app.services.anotherService', []).factory('anotherService', () => {
    return new MyModule.AnotherService();
});

または、グローバルスコープに単一のものを残さないようにするには、クラスの構文を避けて「プレーンな古いJavaScript」を使用できます。

angular.module('app.services.helloService', []).factory('helloService', () => {
    var HelloService = (function () {
        function HelloService() {
        }
        HelloService.prototype.getWelcomeMessage = function () {
            return "Hello";
        };
        return HelloService;
    })();

    return new HelloService();
});
6
Fenton