Angular プロジェクト開発では、通常、コンポーネント通信に入力プロパティ バインディングと出力イベント バインディングを使用しますが、入力と出力は親子でのみ使用できます。コンポーネントが情報を伝達します。コンポーネントは、呼び出し関係に基づいてコンポーネント ツリーを形成します。プロパティ バインディングとイベント バインディングのみがある場合、2 つの非直接関係コンポーネントは、各接続ポイント自体を通じて通信する必要があります。仲介者は、実行するいくつかの処理を継続的に実行する必要があります。情報を知る必要はありません (図 1 左)。 Angularで提供される注射可能なサービスは、モジュール、コンポーネント、または命令で提供でき、コンストラクターでの注入と組み合わせることで、この問題を解決できます(図1の正しい)。 【おすすめ関連チュートリアル:「angularチュートリアル」】
図 1 コンポーネント通信モデル
左の図では、親子コンポーネントのみを介して情報を送信します。ノード a とノード b は、通信するために多くのノードを経由する必要があります。ノード c が何らかの構成を通じてノード b を制御したい場合は、それらの間のノードも設定する必要があります。追加の属性またはイベントを使用して、対応する情報を透過的に送信します。右側の図の依存性注入モードのノード c は、ノード a とノード b が通信するためのサービスを提供できます。ノード a はノード c が提供するサービスと直接通信し、ノード b もノード c が提供するサービスと直接通信します。最後に、通信が簡素化され、中間ノードはコンテンツのこの部分に結合されておらず、上位コンポーネントと下位コンポーネントの間で発生する通信を明確に認識しません。
依存性注入 (DI) は、制御の反転 (IOC) 設計パターンを実装する手段ではありません。依存性注入の出現により、手動の過剰結合の問題が解決されます。リソースを使用しないリソース センターやサードパーティによって提供されるのではなく、両方のパーティでリソースを管理すると、多くの利点が得られます。まず、リソースの一元管理により、リソースが構成可能になり、管理が容易になります。第 2 に、リソースを使用する 2 つの当事者間の依存度 (いわゆるカップリング) が軽減されます。
現実の世界に例えると、鉛筆などの製品を購入するとき、鉛筆タイプの製品を購入するための店を見つけるだけでよく、鉛筆がどこで生産されたか、木材と鉛筆の芯がどのように作られているかは気にしません。鉛筆の書き込み機能を完了するために、結合しています。店舗に関しては、適切なチャネルから鉛筆を購入し、リソースの構成可能性を実現できます。
コーディング シナリオと組み合わせると、より具体的には、ユーザーはインスタンスを挿入して使用するためにインスタンス (新しい操作) を明示的に作成する必要がなく、インスタンスの作成はプロバイダーによって決定されます。リソース管理はトークンを介して行われます。プロバイダーやインスタンスの作成は関係ないため、ユーザーはいくつかのローカル注入方法 (トークンの二次構成) を使用して、最終的にインスタンスの置換と依存関係注入モード (AOP) を実現できます。 )相互に補完し合います。
依存関係の注入は、Angular フレームワークの最も重要なコア モジュールの 1 つです。Angular はサービス タイプの注入を提供するだけでなく、コンポーネント ツリー自体が注入の依存関係ツリーであり、関数や値も注入できます。つまり、Angular フレームワークでは、子コンポーネントは親コンポーネントのトークン (通常はクラス名) を通じて親コンポーネントのインスタンスを注入できます。コンポーネントライブラリの開発では、パラメータのマウントや状態共有、さらには親コンポーネントが配置されているノードのDOMの取得など、親コンポーネントをインジェクトすることで対話や通信を実現するケースが非常に多いです。
Angular インジェクションを使用するには、まずその注入解決プロセスを理解する必要があります。 node_modules の解析プロセスと同様に、依存関係が見つからない場合、依存関係は常に親レイヤーにバブルアップして依存関係を見つけます。 Angularの古いバージョン(V6の前)は、注入解析プロセスをマルチレベルモジュールインジェクター、マルチレベルコンポーネントインジェクター、および要素インジェクターに分割します。新しいバージョン(V9の後)は、2レベルのモデルに単純化されています。解析障害後の解析の順序とデフォルト値は、公式コードコメントドキュメント(Provider_Flag)でより明確に説明されています。
図 2 2 レベルのインジェクター (画像ソース) の依存関係検索プロセスは
、コンポーネント/命令、およびコンポーネント/命令レベルで挿入されたコンテンツの提供が、最初にコンポーネント ビュー内の要素の依存関係をルート要素に至るまで検索することを意味します。見つからない場合は、現在のモジュールの要素内で、モジュールの親モジュールの参照 (モジュール参照およびルーティング遅延ロード参照を含む) がルート モジュールおよびプラットフォーム モジュールまで検索されます。
ここでのインジェクターには継承があることに注意してください。要素インジェクターは、親要素のインジェクターのルックアップ関数を作成および継承できます。モジュール インジェクターも同様です。継続的な継承の後は、js オブジェクトのプロトタイプ チェーンに似たものになります。
依存関係解決の優先順位を理解しており、適切なレベルでコンテンツを提供できます。これには、モジュール インジェクションと要素インジェクションの 2 つのタイプがあることはすでにわかっています。
モジュール インジェクター: プロバイダーは @NgModule のメタデータ属性で構成でき、v6 がモジュール名「root」などとして宣言された後に提供される @Injectable ステートメントを使用することもできます。 (実際には、ルート モジュールの上に、Platform と Null という 2 つのインジェクターがあります。ここでは説明しません。)
要素インジェクター: Providers、viewProviders は、コンポーネント @Component のメタデータ属性、またはディレクティブの @ で構成できます。
さらに、実際には、宣言されたモジュール インジェクターの使用に加えて、@Injectable デコレーターを要素インジェクターとして宣言することもできます
。
多くの場合、シングルトンを実装するためにルートで提供されるように宣言されます。メタデータをクラス自体に統合して、この方法でモジュールまたはコンポーネントを直接明示的に宣言しないようにします。コンパイラによって無視される可能性があるため、木を振ることができます。
それを提供する別の方法は、注射を宣言するときに価値を直接与えることです。
これらのメソッドのショートサンドテンプレートは次のとおりです。
@ngmodule({プロバイダー:[ //モジュールインジェクター] }) エクスポートクラスmyModule {}
@component({プロバイダー:[ // 要素インジェクター - コンポーネント], ViewProviders:[ //要素インジェクター - コンポーネントビュー] }) export class myComponent {}
@directive({プロバイダー:[ // 要素インジェクター - ディレクティブ] }) export class myDirective {}
@Injectable({{ curminin:「root」 }) エクスポート クラス MyService {}
エクスポート const MY_INJECT_TOKEN = new InjectionToken<MyClass>('my-inject-token', { providedIn: 'ルート'、 ファクトリー: () => { 新しい MyClass() を返します。 }依存関係の場所を指定するためのオプションが異なる
と
、いくつかの違いが生じ、最終的にはパッケージのサイズ、依存関係を注入できる範囲、および依存関係のライフサイクルに影響します。シングルトン (ルート)、サービス分離 (モジュール)、複数の編集ウィンドウ (コンポーネント) など、さまざまなシナリオに適用できるさまざまなソリューションがあります。不適切な情報の共有や冗長なコードのパッケージ化を避けるために、適切な場所を選択する必要があります。
インスタンスインジェクションのみを提供している場合、Angular フレームワークの依存性インジェクションの柔軟性が発揮されません。 Angular は多くの柔軟な注入ツールを提供します。useClass は新しいインスタンスを自動的に作成し、useValue は既存のインスタンスを再利用でき、useFactory は指定された deps と指定されたコンストラクター パラメーターを使用して関数によって構築されます。クラスのトークンを切り取って、用意した別のインスタンスに置き換えることもできます。最初にトークンを作成して値またはインスタンスを保存し、後で使用する必要があるときに再度置き換えることもできます。ファクトリ関数を使用してインスタンスのローカル情報を別のオブジェクトまたは属性値にマッピングします。ここでのゲームプレイについては、次のケースを通じて説明しますので、ここでは説明しません。公式サイトにも参考になる作例が多数掲載されています。
Angular でのインジェクションは、コンストラクター内でインジェクションすることも、get メソッドを通じてインジェクターに既存のインジェクトされた要素を取得させることもできます。
Angular は、注入時にマークするデコレータの追加をサポートしています。
、「@Self or @Optional @Host? Angular DI デコレータのビジュアル ガイド」という記事があります。これは、親コンポーネントと子コンポーネントの間で異なるデコレータが使用されている場合、最終的には次のような例が発生することを非常に鮮明に示しています。違い。
図 3 注入されたさまざまなデコレータのフィルタリング結果
ホスト ビューと @Host デコレータの中で、@Host が最も理解しにくいかもしれません。@Host の具体的な手順をいくつか示します。 @Host デコレータの公式の説明は
、ホスト要素に到達するまで任意のインジェクタから依存関係を取得することです。
ここでのホストとは、クエリの範囲をホスト要素内に制限します。ホスト要素とは何ですか?コンポーネント B がコンポーネント A のテンプレートで使用されるコンポーネントの場合、コンポーネント A のインスタンスはコンポーネント B のインスタンスのホスト要素です。コンポーネント テンプレートによって生成されるコンテンツはビューと呼ばれます。同じビューでも、コンポーネントが異なれば異なるビューになる場合があります。コンポーネント A が独自のテンプレート スコープ内でコンポーネント B を使用する場合 (図 4 を参照)、A のテンプレート コンテンツによって形成されるビュー (赤いボックスの部分) がコンポーネント A の埋め込みビューであり、コンポーネント B はこのビュー内にあるため、B については、このビューは B のホスト ビューです。デコレータ @Host は検索範囲をホスト ビューに制限します。見つからない場合はバブルアップされません。
図 4 埋め込みビューとホスト ビュー
実際のケースを使用して、依存関係の挿入がどのように機能するか、エラーのトラブルシューティング方法、およびプレイ方法を見てみましょう。
DevUI コンポーネント ライブラリのモーダル ウィンドウ コンポーネントは、モーダル ボックスをポップアップし、カスタム コンポーネントとして構成できるサービス ModalService を提供します。ビジネスの学生は、このコンポーネントの使用時に、パッケージがカスタム コンポーネントを見つけられないというエラーを報告することがよくあります。
たとえば、次のエラーが報告されます。
図 5 ModalService を使用する場合、EditorX を参照するコンポーネントを作成するときにエラーが発生します。ModalService
ソース コードの Open 関数の行 52 と行 95 を分析します。ご覧のとおり、 componentFactoryResolver
が渡されない場合は、ModalService によって挿入されたcomponentFactoryResolver
が使用されます。ほとんどの場合、企業はルート モジュールに DevUIModule を一度導入しますが、現在のモジュールには ModalModule を導入しません。つまり図6の現状はこんな感じです。図6によると、ModalServiceのインジェクターにeditorXmoduleserviceはありません。
図6モジュールサービス提供関係図
インジェクターの継承によると、4つのソリューションがあります。ModalModule
が宣言されているPut EditorXModuleが宣言されているため、インジェクターはEditorXModuleが提供するEditormoduleserviceを見つけることができます - これは最悪のソリューションです。結果として、サブページで使用する必要があるコンテンツが最初の読み込み時にロードされることになり、負荷が悪化します。 FMP(最初の意味のあるペイント)。
EditorXModule を導入し、ModalService を使用するモジュールに ModalService を導入することをお勧めします。推奨できない状況が 1 つだけあります。それは、ModalService の呼び出しが別のトップレベルのパブリック サービスであり、ロードのために不要なモジュールが上位層に配置されることです。
ModalServiceを使用してコンポーネントをトリガーする場合、現在のモジュールのcomponentFactoryResolver
を注入し、それをModalServiceのopen関数パラメータに渡します。実際に使用される場所にはEditorXModuleを導入することをお勧めします。
使用するモジュールでは、検索の挿入の問題を解決する ModalService を手動で提供することをお勧めします。
4 つのメソッドは、実際には、ModalService によって使用されるcomponentFactoryResolver
のインジェクターの内部チェーンにある EditorXModuleService の問題を解決しています。検索チェーンが2つのレベルにあることを確認することにより、この問題を解決できます。
知識ポイントの概要:モジュールインジェクターの継承と検索スコープ。
通常、複数の場所で同じテンプレートを使用する場合、テンプレートを通じて共通部分を抽出します。以前に DevUI Select コンポーネントを開発したとき、開発者は共通部分を抽出したいと考えていました。エラー。
図7これは、cdkvirtualscrollのcdkvirtualscrollviewportを注入する必要があるため、
発見されています
次のクエリ動作が発生し、検索が失敗します。
図 8 要素インジェクターのクエリ チェーンの検索範囲
最終的な解決策: 1) 元のコードの位置を変更しない、または 2) テンプレート全体を埋め込んで検索する必要があります。
図 9 モジュール全体を埋め込むことで、CdkVitualScrollFo が CdkVirtualScrollViewport を見つけられるようになります (解決策 2)
知識ポイントの要約: 要素インジェクターのクエリ チェーンは、静的テンプレートの DOM 要素の祖先です。
このブログ「Angular:Nested Template駆動型フォーム」から確認できません。
フォーム検証を使用するときにも同じ問題が発生しました。図 10 に示すように、いくつかの理由から、3 つのフィールドのアドレスをコンポーネントにカプセル化して再利用します。
図10:フォームの3つのアドレスフィールドをサブコンポーネントにカプセル化し
ngModelGroup
ControlContainer
図11 ngmodelgroup controlcontainerを見つける
ことは
できません。図12 ng_model_group.tsは、ControlContainerの
注入範囲を制限します。
図13 ViewProvidersを使用して、ネストされたコンポーネントの外部プロバイダーの知識ポイントを提供する
知識ポイントの要約:ViewProviderの素晴らしい使用と既存の使用。
、怠zyなロードの
ためにもはやシングルトンではありませんDevUI コンポーネント ライブラリの DragDropModule は個別にパッケージ化されており、このモジュールは DragDropService を提供します。ドラッグ アンド ドロップ命令は、Draggable 命令と Droppable 命令に分けられ、DragDropService を通じて通信します。 本来は同じモジュールを導入し、そのモジュールが提供する Service を利用することで通信できましたが、遅延読み込み後、DragDropModule モジュールが 2 回パッケージ化され、インスタンスも 2 つ分離されてしまいました。この時点で、怠zyなモジュールのドラッグ可能な命令は、DragdropServiceが現時点では同じインスタンスではないため、別の怠zyなモジュールの滴下命令と通信することはできません。
図14モジュールの怠zyは、
providerIn: 'root'
での私たちの要件がシングルトンが必要であることは明らかですコンポーネント ライブラリの DragDropService は、モジュール レベルで直接ルート ドメインを提供するのが良いでしょうか。しかし、よく考えてみると、ここには別の問題もあります。コンポーネント ライブラリ自体は、さまざまな企業が使用するために提供されています。企業によっては、ページ上の 2 つの場所に 2 つの対応するドラッグ アンド ドロップ グループがある場合、それらの企業はリンクされることを望みません。このとき、シングルトンはモジュールに基づく自然な分離を破壊します。
そうなると、ビジネス側でシングルトンの置き換えを実装する方が合理的になります。前述した依存関係クエリ チェーンを思い出してください。要素インジェクターが最初に検索され、見つからない場合はモジュール インジェクターが開始されます。したがって、交換のアイデアは、要素レベルのプロバイダーを提供できるということです。
図 15 拡張メソッドを使用して新しい DragDropService を取得し、ルート レベルで提供されるものとしてマークする
図16同じセレクターを使用して、繰り返しの命令を重ね、コンポーネントライブラリのドラッグ可能な命令とドロップサービスのトークンを、ルートにシングルトンを提供したドラッグドロップグロバルヴァイスに置き換えることができ
ます図 15 と 16 では、ハンドラーは命令を重ねて、DragDropService トークンを独自のグローバル シングルトンのインスタンスに置き換えます。現時点では、グローバル シングルトン DragDropService を使用する必要があるため、これら 2 つの追加命令を宣言してエクスポートするモジュールを導入するだけで、コンポーネント ライブラリの Draggable 命令 Droppable 命令が遅延読み込みモジュール間で通信できるようになります。
知識ポイントの要約: 要素インジェクターはモジュール インジェクターよりも優先されます。
DevUI コンポーネント ライブラリのテーマ設定では、CSS カスタム属性 (CSS 変数) を使用して、ルートの CSS 変数値を宣言し、テーマの切り替えを実現します。 。 1 つのインターフェイスで異なるテーマのプレビューを同時に表示したい場合は、DOM 要素でローカルに css 変数を再宣言して、ローカル テーマの機能を実現できます。以前テーマ ディザ ジェネレーターを作成していたとき、この方法を使用してテーマをローカルに適用しました。
図17ローカルテーマ機能
ですが、CSS変数値をローカルに適用するだけでは不十分です。ローカル変数は、非常に恥ずかしい状況につながります。ローカルテーマコンポーネントのドロップダウンボックスには、外部テーマのスタイルが表示されます。
図18ローカルテーマのコンポーネントは、
テーマが正しくない場合はどうすればよいですか?アタッチメント ポイントをローカル テーマ dom 内に戻す必要があります。
DevUI コンポーネント ライブラリの DatePickerPro コンポーネントのオーバーレイは、Angular CDK のオーバーレイを使用していることがわかっています。一連の分析の後、次のようにインジェクションに置き換えました。
1) まず、図に示すように、OverlayContainer を継承し、独自の ElementOverlayContainer を実装します。下に。
図 19 ElementOverlayContainer のカスタマイズと _createContainer ロジックの置き換え
2) 次に、プレビューのコンポーネント側で新しい ElementOverlayContainer を直接提供し、新しい Overlay が OverlayContainer を使用できるように新しい Overlay を提供します。本来、Overlay と OverlayContainer はルート上に提供されていますが、ここではこの 2 つをカバーする必要があります。
図20 OverlayContainerをカスタムElementLayContainerに置き換え、新しいオーバーレイを提供すると
、ポップアップレイヤーのDOMがコンポーネントとプレビュー要素に正常に接続されます。
図21 CDKのオーバーレイコンテナ
は、一部のコンポーネントとモーダルボックス引き出しベンチ用のカスタムオーバーレイコントイナク型ライブラリにも
成功しています。最後に、ポップアップ レイヤーとその他のポップアップ レイヤーを実現して、ローカル テーマを完全にサポートできます。
知識ポイントの要約: 優れた抽象化パターンにより、モジュールを置き換え可能にし、エレガントなアスペクト プログラミングを実現できます。
最後のケースに対処する方法として、あまり形式的でないアプローチについて説明したいと思います
。プロバイダーの性質を誰もが理解しやすくするため、プロバイダーを構成するということは、本質的に、プロバイダーをインスタンス化または既存のインスタンスへのマッピングに役立てることを意味します。
cdkOverlay が使用されている場合、ポップアップ ボックスをスクロール バーに追従させ、正しい位置で停止させたい場合は、スクロール バーに cdkScrollable 命令を追加する必要があることがわかります。
これも前の例と同じシナリオです。わかりやすくするために、コンポーネントのホストにスクロール バーを書きました。
図22 Overflow:Auto in the Hostは、
<app-theme-picker-customize></app-theme-picker-customize>
困難な問題に遭遇します<app-theme-picker-customize></app-theme-picker-customize>
の場合、 cdkScrollable 命令を追加するにはどうすればよいですか?解決策は次のとおりです。ここではコードの一部が非表示になり、コア コードのみが残ります。
図23ここでは、インスタンスを注入してライフサイクルを手動で呼び出します。cdkscrollable
のインスタンスは、コンポーネントのライフサイクル段階で同期と呼ばれます。
この解決策は正式な方法ではありませんが、問題は解決されます。読者が試してみるためのアイデアと探索としてここに残しておきます。
知識ポイントの概要:依存関係インジェクション構成プロバイダーはインスタンスを作成できますが、インスタンスは通常のサービスクラスとして扱われ、完全なライフサイクルを持つことはできないことに注意してください。
このブログ投稿を参照してください。「ターミナルでの角度アプリケーションのレンダリング」
図24 RendererFactory2レンダラーとその他のコンテンツを交換して、Angularが
RendererFactory2およびその他のレンダラーをターミナルで置き換えました。これが Angular 設計の柔軟性であり、プラットフォームさえも交換可能です。交換の詳細については元の記事を参照してください。ここでは詳しく説明しません。
知識ポイントの概要:依存関係注入の力は、プロバイダーが単独でそれを構成し、最後に交換ロジックを実装できることです。
この記事では、制御反転の依存関係注入モードとその利点について紹介し、Angular での依存関係注入が依存関係を検索する方法、プロバイダーの構成方法、制限付きデコレータとフィルタリング デコレータを使用して目的のインスタンスを取得する方法、さらに N を使用する方法についても紹介しました。ケースは、依存関係注入の知識ポイントを組み合わせて、開発とプログラミングで遭遇した問題を解決する方法を分析します。
依存関係検索プロセスを正しく理解することにより、正確な場所(ケース1および2)でプロバイダーを構成し、他のインスタンスをシングルトン(ケース4および5)に置き換え、ネストされたコンポーネントパッケージの制限全体に接続できます。ケース 3)、または提供されたメソッド カーブを使用して命令のインスタンス化を実装します (ケース 6)。
ケース5は単純な代替品のようですが、交換できるコード構造を記述できるためには、抽象化が適切でない場合は、インジェクションモードとより良い抽象化が必要です注射の効果を最大限に発揮できません。インジェクション モードでは、モジュールがプラグ可能、プラグイン、およびパーツベースになるためのより多くのスペースが提供され、結合が減少し、柔軟性が向上するため、モジュールがよりエレガントかつ調和して連携できるようになります。
強力な依存関係は、コンポーネントの通信パスを最適化するだけでなく、制御の反転を実現し、カプセル化されたコンポーネントをプログラミングのより多くの側面にさらすこともできます。