web-dev-qa-db-ja.com

AngularJSでディレクティブを作成する場合、新しいスコープ、新しい子スコープ、または新しい分離スコープが不要かどうかをどのように判断すればよいですか?

新しいディレクティブを作成するときに使用するスコープの種類を決定するために使用できるガイドラインを探しています。理想的には、一連の質問を通り抜けて正しい答えを出すフローチャートに似たものが欲しいのですが、新しい新しいスコープ、新しい子スコープ、または新しい分離スコープはありませんが、あまりにも多くを求めている可能性があります。これが私の現在の取るに足らないガイドラインです。

要素の隔離されたスコープでディレクティブを使用すると、同じ要素の他のすべてのディレクティブが同じ(1つの)隔離スコープを使用するように強制されるので、隔離スコープを使用できる場合、これは厳しく制限されませんか?

Angular-UIチームの一部(または多くのディレクティブを作成した他の人)が彼らの経験を共有できることを望んでいます。

「再利用可能なコンポーネントに分離スコープを使用する」という単純な答えを追加しないでください。

265
Mark Rajcok

なんて素晴らしい質問でしょう! love他の人の意見を聞きたいと思いますが、ここに私が使用するガイドラインがあります。

高高度の前提:スコープは、親コントローラー、ディレクティブ、およびディレクティブテンプレート間の通信に使用する「接着剤」として使用されます。

親スコープ:scope: falseなので、新しいスコープはまったくありません

私はこれをあまり頻繁に使用しませんが、@ MarkRajcokが言ったように、ディレクティブがスコープ変数にアクセスしない場合(そして明らかに設定しない!)、これは私が懸念している限りでは問題ありません。これは、親ディレクティブのコンテキストでonlyのみ使用される子ディレクティブ(これには常に例外があります)にも役立ちます。テンプレートがあります。基本的に、テンプレートを持つものはスコープを共有するものではありません。なぜなら、あなたはそのスコープをアクセスと操作のために本質的に公開しているからです(ただし、このルールには例外があると確信しています)。

例として、私は最近、作成中のSVGライブラリを使用して(静的な)ベクトルグラフィックを描画するディレクティブを作成しました。それは$observesの2つの属性(widthおよびheight)であり、それらを計算で使用しますが、スコープ変数を設定も読み取りもせず、テンプレートもありません。これは、別のスコープを作成しないための良い使用例です。必要ないので、なぜわざわざ?

しかし、別のSVGディレクティブでは、使用するデータのセットが必要であり、さらにわずかな状態を保存する必要がありました。この場合、親スコープを使用することは無責任です(これも一般的に言えば)。代わりに...

子スコープ:scope: true

子スコープを持つディレクティブはコンテキストを認識し、現在のスコープと対話することを目的としています。

明らかに、分離スコープに対するこれの主な利点は、ユーザーが任意の属性で補間を自由に使用できることです。例えば分離スコープを持つディレクティブでclass="item-type-{{item.type}}"を使用すると、デフォルトでは機能しませんが、補間されるものはデフォルトで親スコープで見つかるため、子スコープを持つものでは正常に機能します。また、ディレクティブ自体は、親の汚染や損傷を心配することなく、独自のスコープのコンテキストで属性と式を安全に評価できます。

たとえば、ツールチップは追加されるだけのものです。ここでは他のディレクティブまたは補間された属性を使用することが予想されるため、分離スコープは機能しません(デフォルトでは、以下を参照)。ツールチップは単なる機能強化です。しかし、ツールチップは、サブディレクティブおよび/またはテンプレートで使用するためにスコープにいくつかのことを設定する必要があり、明らかにそれ自体の状態を管理するため、親スコープを使用することは非常に悪いでしょう。私たちはそれを汚染するかそれを損なうかのどちらかです。

私は、子スコープを分離スコープまたは親スコープよりも頻繁に使用していることに気付きました。

分離範囲:scope: {}

これは再利用可能なコンポーネント用です。 :-)

しかし真剣に、私は「再利用可能なコンポーネント」を「自己完結型コンポーネント」と考えています。意図は、特定の目的に使用されることであるため、他のディレクティブと組み合わせたり、他の補間属性をDOMノードに追加することは本質的に意味がありません。

具体的には、このスタンドアロン機能に必要なものはすべて、親スコープのコンテキストで評価される指定された属性を通じて提供されます。これらは、一方向の文字列( '@')、一方向の式( '&')、または双方向の変数バインディング( '=')のいずれかです。

自己完結型のコンポーネントでは、それ自体が存在するため、他のディレクティブまたは属性を適用する必要はありません。スタイルは独自のテンプレートによって管理され(必要な場合)、適切なコンテンツをトランスクルードすることができます(必要な場合)。スタンドアロンであるため、「これを混乱させないでください。これらのいくつかの属性を介して定義済みのAPIを提供します」と言うために、分離スコープに入れます。

ベストプラクティスは、ディレクティブリンクおよびコントローラー機能から可能な限りテンプレートベースのものを除外することです。これにより、別の「APIに似た」構成ポイントが提供されます。ディレクティブのユーザーは、単にテンプレートを置き換えることができます!機能はすべて同じままで、その内部APIは変更されませんでしたが、必要なだけスタイリングとDOM実装を台無しにすることができます。 ui/bootstrapはgreatPeter&Pawelが素晴らしいので、これをうまく行う方法の例です。

分離スコープは、トランスクルージョンでの使用にも最適です。タブを取る;それらは機能全体だけでなく、insideが何であれ、タブ(およびペイン)を残したまま親スコープ内から自由に評価できます。彼らが望むものは何でもします。タブには明らかに独自のstateがあり、これはスコープに属し(テンプレートと対話するため)、その状態はコンテキスト内のコンテキストとは関係ありません使用された-それは、タブディレクティブをタブディレクティブにするものの完全に内部的なものです。さらに、タブで他のディレクティブを使用することはあまり意味がありません。それらはタブです-そして、私たちはすでにその機能を持っています!

より多くの機能でそれを囲むか、より多くの機能を除外しますが、ディレクティブはすでにそれです。

とは言っても、@ ProLoserが彼の答えで示唆したように、分離スコープのいくつかの制限(つまり機能)を回避する方法があることに注意する必要があります。たとえば、子スコープのセクションで、分離スコープを使用する場合のデフォルトでの非ディレクティブ属性の破壊に関する補間について説明しました。しかし、ユーザーは、たとえば、単にclass="item-type-{{$parent.item.type}}"を使用するだけで、再び機能します。したがって、子スコープよりも分離スコープを使用する説得力のある理由があるが、これらの制限の一部が心配な場合は、必要に応じて事実上すべてを回避できることを知ってください。

概要

新しいスコープのないディレクティブは読み取り専用です。それらは完全に信頼されており(つまり、アプリの内部)、ジャックに触れません。子スコープを持つディレクティブadd機能ですが、それらはそうではありません唯一の機能。最後に、分離スコープは、目標全体であるディレクティブ用です。それらはスタンドアロンであるため、それらを不正にさせても大丈夫です(そしてほとんどの「正しい」)。

最初の考えを明らかにしたかったのですが、もっと多くのことを考えると、これを更新します。しかし、神聖ながらくた-これはSOの回答には長い...


PS:完全に接線ですが、スコープについて話しているので、「プロトタイプ」と言いますが、他の人は「プロトタイプ」を好みます。 :-)

289

私の個人的なポリシーと経験:

分離:プライベートサンドボックス

私のディレクティブでのみ使用され、ユーザーに表示されたり直接アクセスされたりしないスコープメソッドと変数を多数作成したいと思います。使用可能なスコープデータをホワイトリストに登録したい。 トランスクルージョンを使用して、ユーザーが親スコープ(影響を受けていない状態)に戻ることができますNOT私は、トランスクルードされた子で変数とメソッドにアクセスできるようにします。

子:コンテンツのサブセクション

ユーザーがCANにアクセスできるが、ディレクティブのコンテキスト外の周囲のスコープ(兄弟および親)には関係しないスコープメソッドと変数を作成したい。また、すべての親スコープデータを透過的に細流化したいと思います。

なし:シンプルな読み取り専用ディレクティブ

スコープのメソッドや変数をいじる必要はありません。私はおそらくスコープに関係のないことをしているでしょう(単純なjQueryプラグインの表示、検証など)。

ノート

  • NgModelやその他の要素が決定に直接影響を与えないようにしてください。 ng-model=$parent.myVal(子)またはngModel: '='(分離)のようなことを行うことで、奇妙な動作を回避できます。
  • Isolate + transcludeは、すべての通常の動作を兄弟ディレクティブに復元し、親スコープに戻ります。そのため、判断に影響を与えないでください。
  • noneのスコープを混乱させないでください。これはDOMの下半分のデータをスコープに置くようなものですが、上半分では意味がありません。
  • ディレクティブの優先順位に注意してください(これが物事にどのように影響するかの具体的な例はありません)
  • サービスを注入するか、コントローラーを使用して、任意のスコープタイプのディレクティブ間で通信します。 require: '^ngModel'を実行して、親要素を調べることもできます。
52
ProLoser

多数のディレクティブを作成した後、使用するisolatedスコープを少なくすることにしました。クールで、データをカプセル化し、親スコープにデータを漏らさないようにしますが、一緒に使用できるディレクティブの量は厳しく制限されます。そう、

記述しようとしているディレクティブが単独で完全に動作し、他のディレクティブと共有しない場合は、分離スコープ。 (コンポーネントをプラグインするだけで、最終開発者向けのカスタマイズはあまり必要ありません)(ディレクティブを含むサブエレメントを作成しようとすると、非常に複雑になります)

記述しようとしているディレクティブがjustの場合、スコープの内部状態や明示的なスコープ変更(ほとんどの場合非常に単純なもの)を必要としないdom操作を行います。 no new scopeを探します。 (ngShowngMouseHoverngClickngRepeatなど)

作成するディレクティブが親スコープの一部の要素を変更する必要があるが、内部状態を処理する必要がある場合は、新しい子スコープに進みます。 (ngControllerなど)

ディレクティブのソースコードを必ず確認してください: https://github.com/angular/angular.js/tree/master/src/ng/directive
それは彼らについて考える方法に大いに役立ちます

18
Umur Kontacı

現在の理解と、それが他のJSコンセプトとどのように関連するかを追加すると思いました。

デフォルト(例:宣言されていない、またはスコープ:false)

これは、グローバル変数を使用することと哲学的に同等です。ディレクティブは親コントローラー内のすべてにアクセスできますが、同時にそれらにも影響を及ぼしています。

scope:{}

これはモジュールのようなもので、使用するものはすべて明示的に渡す必要があります。使用するすべてのディレクティブが分離スコープである場合、すべての依存関係を注入する際に多くのオーバーヘッドを伴う独自のモジュールを作成するすべてのJSファイルを作成することと同等になります。

scope:child

これは、グローバル変数と明示的なパススルーの中間です。これは、javascriptのプロトタイプチェーンに似ており、親スコープのコピーを拡張するだけです。分離スコープを作成し、親スコープのすべての属性と機能を渡す場合、機能的にはこれと同等です。


重要なのは、どのディレクティブもどの方法でも記述できることです。さまざまなスコープ宣言は、整理しやすくするためのものです。すべてをモジュールにすることも、すべてのグローバル変数を使用して非常に注意することもできます。メンテナンスを容易にするために、ロジックを論理的に一貫した部分にモジュール化することをお勧めします。オープンな牧草地と閉じた刑務所の間にバランスがあります。 これがトリッキーな理由は、人々がこれについて学ぶとき、彼らはディレクティブがどのように機能するかを学んでいると思うが、実際にはコード/ロジック組織について学んでいるからだと思います

ディレクティブがどのように機能するかを理解するのに役立ったもう1つのことは、ngIncludeについて学ぶことです。 ngIncludeは、htmlパーシャルを含めるのに役立ちます。最初にディレクティブを使用し始めたとき、テンプレートオプションを使用してコードを削減できることがわかりましたが、実際にはロジックを追加していませんでした。

もちろん、angularのディレクティブと angular-ui チームの作業の間では、実質的なことを行う独自のディレクティブを作成する必要がなかったため、これに対する私の見解は完全に間違っている可能性があります。

9
user2483724

私はUmurに同意します。理論的には、孤立したスコープは素晴らしいと「ポータブル」に聞こえますが、非自明な機能を含むアプリを構築する際に、いくつかのディレクティブ(他のディレクティブにネストしたり、属性を追加したりする)を組み込む必要がありましたドメイン固有言語の目的である独自のHTML。

最後に、ディレクティブの各DOM呼び出しで複数の属性を持つすべてのグローバル値または共有値をチェーンに渡す必要があるのはあまりにも奇妙です(isolateスコープで必要です)。 DOMにすべてを繰り返し書くのは愚かに見えるだけで、たとえそれらが共有オブジェクトであっても、非効率だと感じます。また、ディレクティブ宣言を不必要に複雑にします。 $ parentを使用してディレクティブHTMLから値を「取得」し、取得する回避策は、非常に悪い形式のようです。

私も、アプリを変更して、ほとんど分離子を持たない子スコープディレクティブをほとんど持つようにしました。単純な非反復属性を介して渡すことができるもの以外は、親からANYTHINGにアクセスする必要のないもののみです。

そのようなことが起こる前に何十年もドメイン固有言語の夢を夢見ていた私は、AngularJSがこのオプションを提供していることを喜んでおり、この分野でより多くの開発者が働くにつれて、いくつかの非常にクールなアプリが見られることを知っていますアーキテクトにとっても、作成、拡張、デバッグが容易です。

-D

2
Ungallery