angular形状を表すクラスがあります。コンストラクターを使用して、そのクラスの複数のインスタンスをインスタンス化できるようにしたいです。
コンストラクターは、その形状のプロパティを表す複数の引数を取ります。
constructor(public center: Point, public radius: number, fillColor: string,
fillOpacity: number, strokeColor: string, strokeOpacity: number, zIndex: number)
クラス内では、地図上に図形を描画する機能を提供するサービスを使用します。そのサービスをクラスにインジェクトし、コンストラクタを標準的な方法で使用することは可能ですか?.
だから私は以下のようなことをして、Angular自動的に注入された依存関係を解決します.
constructor(public center: GeoPoint, public radius: number,
fillColor: string, fillOpacity: number, strokeColor: string, strokeOpacity: number,
zIndex: number, @Inject(DrawingService) drawingService: DrawingService)
私は問題を解決することができました。
Angular 2-4は、コンストラクターパラメーターの外部に依存関係を注入できる反射型インジェクターを提供します。
@angular/core
からReflectiveインジェクターをインポートするだけでした。
import {ReflectiveInjector} from '@angular/core';
その後:
let injector = ReflectiveInjector.resolveAndCreate([DrawingService]);
this.drawingApi = injector.get(DrawingService);
クラスは@Injectable
デコレータで装飾する必要さえありません。唯一の問題は、DrawingServiceのすべての依存関係と、ネストされたすべての依存関係を提供する必要があるため、維持するのが難しいことです。
[〜#〜] edit [〜#〜]:
角度5
import { Injector } from "@angular/core";
const injector = Injector.create([
{ provide: DrawingService }
]);
this.drawingApi = injector.get(DrawingService);
角度6
import { Injector } from "@angular/core";
const injector = Injector.create({
providers: [
{ provide: DrawingService },
]
});
this.drawingApi = injector.get(DrawingService);
望ましいまたは非常に類似した結果を達成するための2つの可能な方法を次に示します。
オブジェクトのインスタンス化を担当する1つ以上のファクトリサービスがあります。
これは、itがオブジェクトに必要な依存関係をさらに提供し、それらを自分で渡す必要がないことを意味します。
たとえば、エンティティがクラス階層として存在するとします。
abstract class Entity { }
class SomeEntity extends Entity {
...
}
その後、サービスであり、エンティティを構築できるEntityManagerを持つことができます。
@Injectable() // is a normal service, so DI is standard
class EntityManager {
constructor(public http: Http) { } // you can inject any services now
create<E extends Entity>(entityType: { new(): E; }): E {
const entity = new entityType(); // create a new object of that type
entity.manager = this; // set itself on the object so that that object can access the injected services like http - one can also just pass the services not the manager itself
return entity;
}
}
必要に応じて、構築パラメーターを使用することもできます(ただし、create
はすべてのタイプのエンティティを処理する必要があるため、タイプ情報はありません):
class SomeEntity extends Entity {
constructor(param1, param1) { ... }
}
// in EntityManager
create<E extends Entity>(entityType: { new(): E; }, ...params): E {
const entity = new entityType(...params);
...
}
エンティティはマネージャーを宣言できます:
abstract class Entity {
manager: EntityManager;
}
そして、あなたのエンティティはそれを使って何でもできます:
class SomeEntity extends Entity {
doSomething() {
this.manager.http.request('...');
}
}
エンティティ/オブジェクトを作成する必要があるたびに、このマネージャーを使用します。 EntityManager
自体を挿入する必要がありますが、エンティティはフリーオブジェクトです。しかし、すべてのangularコードはコントローラーまたはサービスなどから開始されるため、マネージャーを注入することが可能になります。
// service, controller, pipe, or any other angular-world code
constructor(private entityManager: EntityManager) {
this.entity = entityManager.create(SomeEntity);
}
このアプローチは、任意のオブジェクトに適応させることもできます。クラス階層は必要ありませんが、TypeScriptを使用する方がうまく機能します。また、特にドメイン/オブジェクト指向のアプローチでは、この古い方法でコードを再利用できるため、オブジェクトの基本クラスを用意することも理にかなっています。
[〜#〜] pros [〜#〜]:このアプローチは、完全なDI階層に存在し、望ましくない副作用が少ないため、より安全です。
[〜#〜] cons [〜#〜]:欠点は、new
を二度と使用できないこと、および任意のコードでこれらのサービスにアクセスできないことです。あなたは常にDIとあなたの工場/工場に頼る必要があります。
オブジェクトで必要なサービスを(DIを介して)取得するための専用のサービスを作成します。
この部分は最初のアプローチと非常によく似ていますが、このサービスはファクトリではありません。代わりに、注入されたサービスを別のファイルでこのクラスの外部で定義されたオブジェクトに渡します(後の説明)。例えば:
...
import { externalServices } from './external-services';
@Injectable()
export class ExternalServicesService {
constructor(http: Http, router: Router, someService: SomeService, ...) {
externalServices.http = http;
externalServices.router = router;
externalServices.someService = someService;
}
}
サービスを保持するオブジェクトは、独自のファイルで次のように定義されます。
export const externalServices: {
http,
router,
someService
} = { } as any;
サービスはタイプ情報を使用していないことに注意してください(これは欠点ですが、必要です)。
次に、ExternalServicesService
が1回挿入されることを確認する必要があります。最適な場所は、メインアプリコンポーネントを使用することです。
export class AppComponent {
constructor(..., externalServicesService: ExternalServicesService) {
最後に、メインのアプリコンポーネントがインスタンス化された後、いつでも任意のオブジェクトでサービスを使用できます。
import { externalServices } from '../common/externalServices' // or wherever is defined
export class SomeObject() {
doSomething() {
externalServices.http().request(...) // note this will be called after ng2 app is ready for sure
}
}
クラスコードまたはアプリのインスタンス化後にインスタンス化されていないオブジェクトでは、これらのサービスを呼び出すことはできません。しかし、典型的なアプリでは、これは決して必要ではありません。
今、この奇妙な設定に関するいくつかの説明:
同じファイルの代わりに別のファイルでオブジェクトexternalServices
を使用する理由、または単にクラス自体にサービスを(静的属性として)保存するのはなぜですか?
その理由は、コードを拡張しているときです。 --prod
モードのangular-cli/webpackを介して、正しく解決できない循環依存関係を取得する可能性が非常に高く、見つけるのが難しいerrorsいエラーが発生します-私はすでにこれを経験しています:).
フォームのエラー
未定義のプロパティ「プロトタイプ」を読み取ることができません
--prod
フラグを指定して実行する場合にのみ表示され、依存関係が正しく解決されないという事実を示唆します。
ExternalServicesService
がexternalServices
のみに依存してサービスインスタンスを渡し、アプリケーションがExternalServicesService
を一度だけ(たとえば、メインAppComponentに)注入し、その後すべてがコード/オブジェクトは、externalServices
のみを使用してサービスを取得します。
したがって、そのようなコードは、それ以上の依存関係がないexternalServices
をインポートするだけで済みます(サービスも入力されていないため)。 ExternalServicesService
をインポートする場合、他のすべてをインポートし、双方向の依存関係を静的に解決することはできませんでした。そして、これはng2/webpackでprodにバンドルすると大きな問題になります。
imports
が必要になるため、サービスに型を使用する場合も同じことが起こります。
[〜#〜] pros [〜#〜]:このアプローチは、セットアップが完了すると簡単に使用でき、new
を自由に使用できます。基本的に、どのコードファイルでもexternalServices
をインポートでき、この方法で公開したいサービスにすぐにアクセスできます。
CONS:マイナス面は、ハック的なセットアップと、周期的な依存によって引き起こされる可能性のある問題です。 externalServices
がこれらのサービスをすぐに使用できるかどうかわからないので、より敏感です。これらは、ng2アプリが起動し、ExternalServicesService
が最初に挿入された後にのみ定義されます。欠点は、それらのサービスのタイプ情報がもうないことです。
PS:このトピックがそれほど人気がない理由はわかりません。
たとえば、ドメイン指向設計のファンであること、強力なエンティティ(たとえば、REST呼び出しまたは他のサービスとのやり取り)に向けられたメソッドを使用すること)が重要であり、この制限により常に困難になりました。
アンギュラjsとAngular2 +の両方でこの制限を克服する必要がありましたが、それはまだライブラリで対処されていないようです。
Angular 5.x:
import { Injector } from "@angular/core";
export class Model {
static api: Api;
constructor(data: any) {
// check the api ref not exist
// We don't want to initiate a new object every time
if (!Model.api){
//try inject my api service which use the HttpClient
const injector: any = Injector.create([{ provide: Api, useClass: Api, deps: [] }]);
Model.api = injector.get(Api);
}
// .....
}
}
GitHubにvojtajinaからの投稿があり、この問題に対する素晴らしいアプローチを提供しています。この答えは単なるリンクですが、他の興味深い情報がそこにあるので、コンテキストでこれを読むことは本当に良いです:
https://github.com/angular/di.js/issues/22#issuecomment-3677334