すべてのサービスをインジェクトするangular2コンポーネントのセットがあります。私が最初に考えたのは、スーパークラスを作成してそこにサービスを注入することが最善だろうということでした。私のコンポーネントはいずれもそのスーパークラスを拡張しますが、このアプローチは機能しません。
簡単な例:
export class AbstractComponent {
constructor(private myservice: MyService) {
// Inject the service I need for all components
}
}
export MyComponent extends AbstractComponent {
constructor(private anotherService: AnotherService) {
super(); // This gives an error as super constructor needs an argument
}
}
あらゆるコンポーネントにMyService
を挿入し、super()
呼び出しにその引数を使用することでこれを解決できましたが、それは明らかにある種の不条理です。
スーパークラスからサービスを継承するようにコンポーネントを正しく整理する方法は?
ソリューションを更新し、グローバルインジェクターを使用してmyServiceの複数のインスタンスが生成されるのを防ぎます。
import {Injector} from '@angular/core';
import {MyServiceA} from './myServiceA';
import {MyServiceB} from './myServiceB';
import {MyServiceC} from './myServiceC';
export class AbstractComponent {
protected myServiceA:MyServiceA;
protected myServiceB:MyServiceB;
protected myServiceC:MyServiceC;
constructor(injector: Injector) {
this.settingsServiceA = injector.get(MyServiceA);
this.settingsServiceB = injector.get(MyServiceB);
this.settingsServiceB = injector.get(MyServiceC);
}
}
export MyComponent extends AbstractComponent {
constructor(
private anotherService: AnotherService,
injector: Injector
) {
super(injector);
this.myServiceA.JustCallSomeMethod();
this.myServiceB.JustCallAnotherMethod();
this.myServiceC.JustOneMoreMethod();
}
}
これにより、すべての派生クラスにMyServiceを注入することなく、AbstractComponentを拡張するクラス内でMyServiceを使用できるようになります。
このソリューションにはいくつかの短所があります(元の質問の下にある@GünterZöchbauerのコメントを参照してください)。
Angular2での依存性注入の非常によく書かれた説明については、このブログ投稿を参照してください。問題を解決するのに大いに役立ちました。 http://blog.thoughtram.io/angular/2015/05/18/dependency-injection- in-angular-2.html
私はこれをすべてのコンポーネント内にMyServiceを注入し、super()呼び出しにその引数を使用することで解決できましたが、それは明らかにある種の馬鹿げたものです。
それは不条理ではありません。これが、コンストラクターとコンストラクターインジェクションの仕組みです。
すべての注入可能クラスは、コンストラクターのパラメーターとして依存関係を宣言する必要があり、スーパークラスにも依存関係がある場合、サブクラスのコンストラクターにもリストし、super(dep1, dep2)
呼び出しでスーパークラスに渡す必要があります。
インジェクターを迂回して依存関係を取得することは、重大な欠点があります。
依存関係を隠し、コードを読みにくくします。
Angular2 DIの仕組みに精通している人の期待に反します。
静的なコードを生成するオフラインコンパイルを中断して、宣言型および命令型DIを置き換え、パフォーマンスを向上させ、コードサイズを削減します。
すべてのサービスを手動で注入する代わりに、サービスを提供するクラスを作成しました。たとえば、サービスを注入します。次に、このクラスは派生クラスに注入され、基本クラスに渡されます。
派生クラス:
@Component({
...
providers: [ProviderService]
})
export class DerivedComponent extends BaseComponent {
constructor(protected providerService: ProviderService) {
super(providerService);
}
}
基本クラス:
export class BaseComponent {
constructor(protected providerService: ProviderService) {
// do something with providerService
}
}
サービス提供クラス:
@Injectable()
export class ProviderService {
constructor(private _apiService: ApiService, private _authService: AuthService) {
}
}
以下のように、依存関係として他のすべてのサービスを持つサービスを注入する代わりに:
class ProviderService {
constructor(private service1: Service1, private service2: Service2) {}
}
class BaseComponent {
constructor(protected providerService: ProviderService) {}
ngOnInit() {
// Access to all application services with providerService
this.providerService.service1
}
}
class DerivedComponent extends BaseComponent {
ngOnInit() {
// Access to all application services with providerService
this.providerService.service1
}
}
この追加手順をスキップして、次のようにBaseComponentにすべてのサービスを挿入するだけです。
class BaseComponent {
constructor(protected service1: Service1, protected service2: Service2) {}
}
class DerivedComponent extends BaseComponent {
ngOnInit() {
this.service1;
this.service2;
}
}
この手法は2つのことを前提としています。
あなたの懸念は、コンポーネントの継承に完全に関連しています。ほとんどの場合、この質問にたどり着いた理由は、各派生クラスで繰り返す必要がある非ドライ(WET?)コードが圧倒的に多いためです。すべてのコンポーネントおよびサービスに対して単一のエントリポイントを利用したい場合は、追加の手順を実行する必要があります。
すべてのコンポーネントはBaseComponent
を拡張します
派生クラスのコンストラクターを使用することを決定した場合にも、super()
を呼び出してすべての依存関係を渡す必要があるため、欠点があります。 constructor
の代わりにngOnInit
の使用を必要とするユースケースは実際にはありませんが、そのようなユースケースが存在する可能性は完全にあります。
親クラスがサードパーティのプラグインから取得されている場合(そしてソースを変更できない場合)、これを行うことができます:
import { Injector } from '@angular/core';
export MyComponent extends AbstractComponent {
constructor(
protected injector: Injector,
private anotherService: AnotherService
) {
super(injector.get(MyService));
}
}
または最も良い方法(コンストラクターにパラメーターを1つだけ保持):
import { Injector } from '@angular/core';
export MyComponent extends AbstractComponent {
private anotherService: AnotherService;
constructor(
protected injector: Injector
) {
super(injector.get(MyService));
this.anotherService = injector.get(AnotherService);
}
}
基本クラスから継承するために私が理解していることから、最初にインスタンス化する必要があります。インスタンス化するには、コンストラクタに必要なパラメータを渡す必要があります。したがって、子から親にsuper()呼び出しを介して渡すので、意味があります。もちろん、インジェクターも実行可能なソリューションです。