web-dev-qa-db-ja.com

Angular 2 + / 4/5/6/7:スマート、ダム、深くネストされたコンポーネント通信

注:簡単にするために、コンポーネントの深さを次のように考慮してください。

- Smart (grand)parent level 0
  - dumb child level 1
   ....
    - dumb grandchild level 2
      ....)

スマート/グランド/親/子コンポーネントがマルチレベル(少なくとも3レベル)チェーンでデータをやり取りして渡す方法には、さまざまなオプションと条件があります。データサービス(またはアトミック/不変ストア)にアクセスできる唯一のコンポーネントとして「スマート」(グランド)親コンポーネントを保持し、「ダム」(グランド)子との情報交換を促進します。表示されるオプションは次のとおりです。

  1. アンチパターン(?):@ Input/@ Outputバインディングを介してデータをコンポーネントチェーンに渡します。これは、「外部プロパティ」または「カスタムイベントバブリングの問題」問題(例: here および here 。)問題と呼ばれるものです。立ち入り禁止。
  2. アンチパターン:@ViewChildrenまたは@ContentChildenを介したダム(グランド)子へのスマートコンポーネントアクセス。これもまた子をハードワイヤードし、(グランド)子がデータをスマートコンポーネントにUPするためのクリーンなメカニズムを作成しません。
  3. Angular.io cookbook here および優れた投稿 here で説明されている共有メッセージサービス。

「3」の場合、愚かな(グランド)子供にはメッセージサービスをインジェクトする必要があります。それは私の質問に私をもたらします:

Q1:「ダム」(グランド)の子供それぞれがメッセージサービスをインジェクトするのは直感的に奇妙に思えます。メッセージサービスがこのファミリ専用のサービスであるためのベストプラクティスはOR上記の「スマート」祖父母が担当するデータサービスに便乗しますか?

Q1A:さらに、すべてのコンポーネントにサービスが注入される場合、チェーンの上下に@ Input/@ Outputバインディングを追加するよりも、これはどのように優れていますか? (「ダム」コンポーネントには情報を取得するための何らかの方法が必要であるという議論があります)

Q2:「スマートな」祖父母がreduxのようなストア(私たちにとってはngrx)と通信していた場合はどうなりますか?繰り返しますが、「ダム」コンポーネントとの通信は、挿入/専用のメッセージサービスを介して行うのが最適ですか、それとも各「ダム」コンポーネントにストアを挿入するのが最適ですか?コンポーネント間通信は、データに加えて「アクション」(例:フォーム検証、無効化ボタンなど)の組み合わせであることに注意してください(ストアまたはサービスへのデータの追加/更新)。

考えは大歓迎です!

24
MoMo

(更新:2019年2月7日:この投稿は日付が付けられていました-「store/ngrx」パターンが追加されました)

したがって、これをさらに検討した後、ネストされたコンポーネントチェーンを上下に通信する最善の方法になると、実際には2つのオプションしかありません-間のファウスト契約

いずれか

  • @ Input/@ Outputバインディングを上、下、ネストされたコンポーネントチェーン全体に渡します(つまり、「カスタムイベントバブリング」または「外部プロパティ」の問題に対処します)

OR

  • メッセージング/サブスクリプションサービスを使用して、このコンポーネントファミリ間で通信し(詳細な説明 ここ )、チェーン内の各コンポーネントにそのサービスを注入します。

または:

  • リアクティブストアパターン(例: 'ngrx')も別のオプションです。 IMO、スマートコンポーネントとダムコンポーネントの概念が引き続き適用されることに注意してください。つまり、ダムコンポーネントはストアに直接アクセスしません。繰り返しますが、スマートコンポーネントは、ストアを介してデータを取得するための主要なパーティです。

私は個人的に、スマートでプレゼンテーション用の(「ダム」)コンポーネントを利用することを支持しています。 「ストア」の追加も選択的に行う必要があります。これは、アーキテクチャ、一貫した実装パターン、開発、メンテナンスから新しい人材のオンボーディングまで、プロセスのコストを大幅に増加させるためです。名目上、「ダム」コンポーネントは@Inputsと@Outputsのみを必要とし、それだけです。コンポーネントツリー内の深さを気にしません。これがアプリケーションの問題です。実際、そもそもどのアプリケーションがそれを使用するかは気にしません。一方、アプリケーション固有のサービスがインジェクトされている場合、ディープダウンコンポーネントはあまり馬鹿げていないし、持ち運びもできません。ところで、カウンターパートの「スマート」コンポーネントは、それを必要とするそのファミリーツリー内のダムコンポーネントに、実際に中間サービスを提供しています(ファーストクラスの@Injectableサービスまたはreduxのようなストアを介して)。また、スマートコンポーネントは、孫が何らかの形でサービス/ストアアクションを行う必要があることを通知する限り、(直接@ Input/@ Outputチェーンを介して)直接の子の@Inputを超えるコンポーネントを気にしません。このようにして、スマートコンポーネントはアプリケーションライン間で転送可能になります。

これを考えると、ファウストの掘り出し物IMOは、@ Input/@ Outputチェーンを使用して、それがもたらす前述のすべての問題に傾倒しています。とはいえ、私はこれに目を光らせており、誰かが知っているなら、クリーンで分離された代替手段を歓迎します。

10
MoMo

なぜ#1がアンチパターンなのですか?祖父母コンポーネントはデータを所有し、@ Inputパラメータを介してダム子コンポーネントに渡します。ダム子コンポーネントは、イベントが発生すると(@Outputイベントエミッターを介して)単にコールバックを呼び出し、祖父母コンポーネントがデータを操作します。私にはきれいなようです。

編集:多くの中間層を介して送信ハンドラーのような値を繰り返し渡すことについてあなたのポイントを見ています。コンポーネントツリーを表すネストされた構造が親コンポーネントに作成される場合があります。次に、各コンポーネントに必要なプロパティと、次のコンポーネントに渡すオブジェクトを渡すことができます。各コンポーネントは、その下のコンポーネントのみを認識します。

// Parent component builds this object (or gets a service to do it)

viewModelForChildComponent: {

    property1NeededForChildComponent,

    property2NeededForChildComponent,

    viewModelForGrandChildComponent: {
        property1NeededForGrandChildComponent,

        property2NeededForGrandChildComponent,

        viewModelForGrandGrandChildComponent: {
            property1NeededForGrandGrandChildComponent,

            submitHandlerNeededForGrandGrandChildComponent
        }
    }
}
1
Frank Modica

Input()およびOutput()バインディングも、これを処理する完全に正当な方法です。スマートコンポーネントに値を生成するロジックを処理させ、Input()およびOutput()を使用して、コンポーネントチェーンに沿って値を単純に受け渡します。

もちろん、これはスマート/ビューアプローチの欠点の1つを指します。よりボイラープレート。だから、私は万能の単一のアプローチを主張しません。むしろ、現在の状況で意味のあるアプローチを選択してください(アプリと組織の両方にとって)。

1
Muirik