web-dev-qa-db-ja.com

AngularJSサービスでのDOM操作

AngularJSを使用する場合は、ディレクティブ内のDOM要素を操作する必要があることはよく知られています。

ただし、一部のユースケースでは、サービス内でDOMを操作することは許容できるようです。 Misko Heveryがこれについて話している ここBootstrap UI Dialog 内にも例があります。

Miskoの説明はかなり曖昧なので、ディレクティブではなくサービス内にDOMを配置する必要がある場合をどのように判断するのか疑問に思いました。

28
Florian F

ディレクティブは、その定義方法とともに、常にDOMノードにアタッチされます。したがって、ディレクティブを定義すると、ディレクティブは、それが接続されているDOMノードを「拡張」または置換します。

特定の状況(ダイアログなど)では、DOMノードを特定の親にアタッチできません。このような場合、サービスを使用することは理にかなっており、DOM操作はサービスにカプセル化されるため、コントローラーはDOMビットの外にとどまることができます。

ポップアップは、おそらくサービスを使用できる別の状況である可能性がありますが、ダイアログとは異なり、ポップアップISはDOMノードに接続されています。したがって、それでもわずかに灰色の領域です。

したがって、基本的で簡単なテストは、「このDOM操作コードのビットをDOMノードにアタッチできますか?」です。はいの場合、ディレクティブ。いいえの場合は、サービスを提供します。

ダイアログとカスタム確認ボックスは、サービスを使用する典型的な例として提供されます。

27
ganaraj

GanarajはMiskoがよく言っていることを説明していると思いますが、モーダルダイアログのDOM操作コード(たとえば)canを置くことができます(そして潜在的にshould) DOMノードに。

1つのアプローチは、ダイアログディレクティブを常にDOMにアタッチし、ng-showを使用して条件付きで表示することです。その後、$rootScope、またはそれ以上のサービスを使用してモーダルダイアログと通信できます。

私は実際にこのアプローチを好みます。なぜなら、サービスはデータの転送を処理し、ディレクティブはサービスと対話して、ユーザーにとって意味のある方法で表示されるようにするからです。

でも、ミスコが特にはっきりしていないということは、かなり主観的だと思います。あなたにとって最も理にかなっていることをしてください。

4
Ed Hinchliffe

@dudewadに同意します。結局のところ、サービス(ファクトリ、プロバイダー、値)は、シングルトンとして実装されるという制限がある、Angularのモジュールパターンにすぎません。ドキュメントやその他のグローバルなアプローチを使用するのではなく、ディレクティブのリンク関数に渡される要素を介してDOMにアクセスすることが重要だと思います。ただし、ディレクティブから取得するdom要素に適用するロジックが同じモジュール内にあることは重要ではないと思います。 SRPの理由から、サービスを使用してコードを少し分割することが望ましい場合があります。これは、特に複雑なロジックを使用してテストに集中したほうが理にかなっている場合や、ロジックを複数のディレクティブで使用したい場合があるためです。 @dudewadが指摘したように。

2
Ryan Vice

私は現在、サービスに特定のロジックを配置したくなるような問題に取り組んでいるため、このトピックをグーグルで調べて、これについての内面的な感覚を強化しています。これが私の場合であり、私が思うのは、DOMベースの処理をサービスに入れるための十分な正当化です。

マウスの位置にグローバルに反応するディレクティブベースの要素があります(たとえば、マウスの位置に基づいて何らかの方法で移動または変更します)。これらの要素の数は不確定であり、アプリケーション内の特定の場所には関係しません(GUI要素であり、どこのコンテナーにも関係する可能性があるため)。厳密なangular "ディレクティブのみのdomロジック"ルール]に準拠する場合、要素はすべてマウス位置の解析に関連するロジックを共有するため、効率が低下します(効率的に)これはwindow.requestAnimationFrameティックを中心に展開します。

そのロジックをディレクティブにバンドルすると、すべてのインスタンスにリスナー/ rafループが関連付けられます。それでもDRYですが、マウスを動かすたびにまったく同じリスナーを起動し、すべての要素に対してまったく同じ結果を返すため、効率的ではありません。

この場合、domベースのロジックであるにもかかわらず、これをサービスに移動し、サービスに対して各ディレクティブインスタンスを登録して、実行されたロジックに対して同じインスタンスベースのロジックを呼び出すのが実際には最善です。

Angularは、コードを構造化する方法について非常に優れたアドバイスを提供しますが、すべてを網羅することはできないため、決して厳格なルールの防弾にはなりません。ユースケース。「ベストプラクティス」が失敗しているように見える穴を見つけた場合、それはあなたが実際に適切に理解しているベストプラクティスであり、ルールを破る理由を見つけたからです。目的。

それは私の2セントです!

2
dudewad

変数の変更に基づくDOM操作メソッド(つまり、_ng-show="isVisible"_)を使用することの1つの欠点は、DOM操作が次の「javascriptturn」ループの後で(isVisibleが更新されたときに)発生することです。 DOMをすぐに更新する必要があるかもしれません。

たとえば、一般的なシナリオは、新しいルート/状態への移行中に「スピナー」を表示することです。 _$scope.isVisible = true_/_$routeChangeStart_イベントに_$stateChangeStart_を設定し、次に_$scope.isVisible = false_/_$routeChangeSuccess_イベントに_$stateChangeSuccess_を設定した場合、決してルート/状態の変更全体が1つのJavaScriptターン内で発生するため、_ng-show_を参照してください。実際にスピナーが表示されるように、これらのイベントでは.show().hide()を使用することをお勧めします。

これをすべて元に戻し、OPの質問に関連させるために、DOM操作が表示される「スピナー」モーダルである状況では、サービス中に実行し、直接DOM操作メソッドを使用して実行します。モデルの変更に頼るのではなく。

1
jlee