しかし、何人かの人々は、彼らがAngularでDIとその機能を理解するのが難しいと私に同意しませんでした。DIを効果的に使用する方法に関する資料はインターネット上にそれほど多くありません。多くの開発者にとって、グローバルサービスを操作するか、アプリケーションのルートからコンポーネントにグローバルデータを渡すことになります。
Angularでこのメカニズムを詳しく見てみましょう。
あなたはあなたの中毒を知っていますか?
コードに依存関係がいくつあるかを理解するのは簡単ではない場合があります。
たとえば、この疑似クラスを見て、依存関係の数を数えます。
import { API_URL } from '../../../env/api-url';
import { Logger } from '../../services/logger';
class PseudoClass {
request() {
fetch(API_URL).then(...);
}
onError(error) {
const logger = new Logger();
logger.log(document.location, error);
}
}
回答
fetch — API, , , .
API_URL — ( ).
new Logger() — , .
document — API .
API_URL — ( ).
new Logger() — , .
document — API .
では、何が問題なのですか?
たとえば、このようなクラスは、他のファイルやその中の特定のエンティティからインポートされたデータに依存するため、テストが困難です。
別の状況:ドキュメントとフェッチはブラウザでシームレスに機能します。ただし、ある日、アプリケーションをサーバー側レンダリングに転送する必要がある場合、必要なグローバル変数がnodejs環境にない可能性があります。
では、DIとは何ですか、なぜそれが必要なのですか?
依存関係インジェクションは、アプリケーション内の依存関係を管理します。基本的に、私たちにとって、Angular開発者に関しては、このシステムは非常に単純です。依存関係ツリーに何かを入れるか、依存関係ツリーから何かを取得するという2つの主要な操作があります。
DIのより理論的な観点については、制御原理の反転についてお読みください。また、このトピックに関する興味深いビデオを見ることができます。ロシア語のIlya KlimovによるIoCとDIに関する一連のビデオ、または英語のIoCに関する短いビデオです。
すべての魔法は、依存関係を提供および取得する順序から発生します。
DIでスコープがどのように機能するか:
DIに何を入れることができますか?
DI操作の最初はそれに何かを入れることです。実際、このために、Angularを使用すると、モジュール、コンポーネント、またはディレクティブのデコレータにプロバイダー配列を書き込むことができます。この配列が何で構成されているか見てみましょう。
クラスの提供
通常、すべてのAngular開発者はこれを知っています。これは、アプリケーションにサービスを追加するときです。
Angularは、最初に要求したときにクラスのインスタンスを作成します。また、Angular 6では、providers配列にクラスを書き込むことはできませんが、DIのどこにprovidedInを使用するかをクラス自体に指示します。
providers: [
{
provide: SomeService,
useClass: SomeService
},
// Angular :
SomeService
]
価値を提供する
定数値は、DIを介して提供することもできます。APIのURLを含む単純な文字列、またはデータを含む複雑なObservableのいずれかです。
値の提供は通常、InjectionTokenと組み合わせて実装されます。このオブジェクトは、DIエンジンのキーです。まず、「このキーのこのデータを取得したい」と言います。そして後でDIに来て、「このキーに何かありますか?」と尋ねます。
一般的なケースは、アプリケーションのルートからグローバルデータを転送することです。
これが実際に動作しているのをすぐに確認したほうがよいので、例を使用してstackblitzを見てみましょう。
例を展開
したがって、この例では、別のファイルから定数として直接インポートするのではなく、DIから依存関係を取得しました。そして、なぜそれが私たちにとって良いのですか?
- トークン値を使用するコンポーネントを変更せずに、DIツリーの任意のレベルでトークン値をオーバーライドできます。
- テスト時に、トークンの値を適切なデータでモックすることができます。
- コンポーネントは完全に分離されており、コンテキストに関係なく常に同じように機能します。
工場の提供
私の意見では、これはAngularの依存関係注入エンジンで最も強力なツールです。
他のトークンの値を組み合わせて変換した結果となるトークンを作成できます。
これは、ストリームを使用してファクトリを作成する詳細な例を含む別のstackbitzです。
例を展開
工場を提供することで時間を節約したり、コードを読みやすくしたりする場合が多くあります。コンポーネントを結合したり、完全に異なる形式に変換したりするためだけに、コンポーネントに依存関係を挿入することがあります。前回の記事では、この問題をより詳細に検討し、そのような状況を解決するための代替アプローチを示しました。
既存のインスタンスを提供する
一般的なケースではありませんが、このオプションは非常に便利なツールです。
すでに作成されているエンティティをトークンに入れることができます。forwardRefでうまく機能します。
インターフェイスを実装し、別のトークンをuseExistingに置き換えるディレクティブを使用したstackblitzの別の例を見てください。この例では、ディレクティブがハングする要素の子コンポーネントのDIのみのトークン値をオーバーライドします。さらに、ディレクティブは任意にすることができます。主なことは、必要なインターフェイスを実装することです。
例を展開
DIデコレータのトリック
DIデコレータを使用すると、DIクエリをより柔軟にすることができます。
4つのデコレータすべてがわからない場合は、Mediumに関するこの記事を読むことをお勧めします。記事は英語ですが、このトピックには非常にクールで理解しやすい視覚化があります。
DIデコレータをdeps配列で使用できることを知っている人はあまりいません。これにより、プロバイダーファクトリの引数が準備されます。
providers: [
{
provide: SOME_TOKEN,
/**
* ,
* [new Decorator(), new Decorator(),..., TOKEN]
* .
*
* ‘null’,
* OPTIONAL_TOKEN
*/
deps: [[new Optional(), OPTIONAL_TOKEN]],
useFactory: someTokenFactory
}
]
トークンファクトリー
InjectionTokenコンストラクターは2つの引数を取ります。
2番目の引数は、トークン構成のオブジェクトです。
トークンファクトリは、誰かがこのトークンを初めて要求したときに呼び出される関数です。その中で、トークンの特定の標準値を計算したり、注入機能を介して他のDIエンティティにアクセスしたりすることもできます。
ボタンクリックストリーム機能の実装例を見てみましょう。ただし、今回はトークンファクトリです。
例を展開
結論
AngularのDIは驚くべきトピックです。一見すると、学ぶためのさまざまなレバーやツールはありませんが、それらがもたらす可能性と使用法について何時間も書いたり話したりすることができます。
この記事が、アプリケーションやライブラリでのデータの操作を簡素化するための独自のソリューションを考え出すための基盤となることを願っています。