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


265

新しいディレクティブを作成するときに使用するスコープのタイプを決定するのに役立つガイドラインを探しています。理想的には、一連の質問を案内して正しい答えを表示するフローチャートに似たものを望みます。新しい新しいスコープ、新しい子スコープ、または新しい分離スコープはありませんが、多すぎると思われます。これが私の現在のちょっとしたガイドラインです:

  • ディレクティブを使用する要素がng-model
    を使用する場合は分離スコープを使用しないでください。分離スコープでng-model を使用できますか?を参照してくださいそして
    なぜフォーマッタは孤立範囲では動作しませんか?
  • ディレクティブがスコープ/モデルプロパティを変更しない場合は、新しいスコープを作成しないでください
  • ディレクティブが一連のDOM要素をカプセル化している場合、分離スコープはうまく機能するようです(ドキュメントは「複雑なDOM構造」と記載されている)、ディレクティブが要素として使用される場合、または同じ要素に他のディレクティブがない。

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

Angular-UIチームのメンバー(または多くのディレクティブを作成した他のチーム)が経験を共有できることを願っています。

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


「子スコープ」とは、「scope。$ new()」によってリンク関数にスコープを作成することを意味しますか?私が知っているように、ディレクティブはスコープを分離することも、しないこともできます(使用されているスコープを使用します)
Valentyn Shybanov

3
@ValentynShybanov設定scope: trueは、$scope.new()自動的に使用して子スコープを作成します。
Josh David Miller、

2
@ Valentyn、Joshの発言:つまり、3つの可能性はscope: false(デフォルト、新しいスコープなし)、scope: true(プロトタイプを継承する新しいスコープ)、およびscope: { ... }(新しい分離スコープ)です。
Mark Rajcok、2013

1
はい、thnx。「true」と「{}」の違いを見逃しました。知っておくと良い。
Valentyn Shybanov 2013

人々が一般に無視する傾向がある4番目のケースがあります。それは「ディレクティブコントローラー」です。質問も拡張して、それらも含める必要があると思います...質問に+1
ganaraj

回答:


291

すばらしい質問ですね。私は思いますが大好き他の人が言っているか聞くために、しかし、ここで私が使用ガイドラインです。

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

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

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

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

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

子スコープ: scope: true

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

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

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

分離スコープや親スコープよりも子スコープをよく使用しています。

スコープを分離: scope: {}

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

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

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

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

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

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

それをより多くの機能で囲むか、より多くの機能を隠蔽しますが、指令はそれがすでにあるものです。

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

概要

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

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


PS:完全に正接ですが、スコープについて話しているので、「プロトタイプ」と言いたいのに対し、他の人は「プロトタイプ」を好んでいます。これはより正確に見えますが、舌から離れているだけです。:-)


ジョシュ、ありがとうございます。私はこれについて長い答えが欲しかった/期待しました。私が従わなかった2つのこと:1)子スコープ:「ユーザーは、必要な属性に対して自由に補間を使用できます」。2)スコープを分離します:「「すべて」ではなく、「?」の場合」それらについて少し詳しく説明できますか?(コメントを書くのが簡単な場合は、投稿を編集するのではなく、自由に編集してください。)
Mark Rajcok

@MarkRajcok(1)については、少し曖昧さを少なくするために変更しました。失敗した場合はお知らせください。(2)の場合、それはタイプミスと不適切な表現の組み合わせでした。私はその段落をより明確にするために書き直しました。また、1つまたは2つの例を追加し、さらにいくつかのことを明確にし、いくつかのタイプミスを修正しました。
Josh David Miller、

回答で述べたように、angularのブートストラップはこれらを組み合わせる良い例です。アコーディオンの例が特に役立つことがわかりました
-GitHub-

子スコープを最もよく使用するとのことですが、ディレクティブの再利用可能なパターンが最も一般的であると私は考え、1度だけ使用することを意図したディレクティブの作成を避けました。これは不要ですか?HTMLが大きくなりすぎて、そのセクションをディレクティブに移動したい場合がありますが、使用されるのは1回だけなので、HTMLのままにしておきます。
user2483724 14年

2
@ user2483724よくある誤解は、「再利用可能な」ディレクティブは分離スコープを使用するディレクティブであるというものです。そうではありません。あらかじめパッケージ化されたディレクティブを見ると、分離スコープを使用するものはほとんどなく、一部は子スコープでさえありませんが、再利用できることは間違いありません。ルールは、ディレクティブ内のスコープがどのように使用されるかにあります。ファイルのスペースを節約するだけの場合は、ディレクティブが最善のアプローチであるかどうかはわかりません。開発者のために処理時間が長くなります。しかし、もし必要なら、それを手に入れてください。またはを使用しngIncludeます。または、ビルドの一部として実行します。多くのオプション!
Josh David Miller、

52

私の個人的な方針と経験:

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

ディレクティブでのみ使用され、ユーザーから直接見られたり、直接アクセスされたりしないスコープメソッドと変数を多数作成したいと考えています。使用できるスコープデータをホワイトリストに登録したい。トランスクルージョンを使用して、ユーザーが親スコープ(影響を受けない)に戻ってジャンプできるようにすることができます。私は自分の変数とメソッドを、隠された子供たちからアクセスできるようにしたくありません

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

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

なし:単純な読み取り専用ディレクティブ

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

ノート

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

1
私はこの部分を誤解しているかもしれません:「分離+トランスクルードはすべての通常の動作を兄弟ディレクティブに復元します」。このプランカーを参照してください。コンソールを確認する必要があります。
Josh David Miller、

1
あなたの洞察/答えをProLoserに感謝します。angularjs-uiタグを追加した場合、あなたは私がこの投稿を見ることを望んでいた人々の1人です。
Mark Rajcok 2013

同じDOM要素のディレクティブについて話すとき@JoshDavidMillerは物事がより複雑になるので、代わりに優先度プロパティを調べ始める必要があります。トランスクルージョンは、子供のコンテンツにより関連しています。
ProLoser 2013

1
@ProLoserその通りですが、その発言が何を意味していたのかはわかりません。それらは明らかに子供に影響しますが、ディレクティブのスコープは兄弟のディレクティブにどのように影響しますか?
Josh David Miller

18

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

作成するディレクティブが完全に単独で動作し、他のディレクティブと共有しない場合は、分離されたスコープを使用してください。(プラグインできるコンポーネントのように、最終的な開発者向けのカスタマイズはあまりありません)(内部にディレクティブを持つサブエレメントを記述しようとすると、非常に扱いにくくなります)

作成するディレクティブが、スコープの内部状態や明示的なスコープ変更(ほとんどの場合非常に単純なもの)を必要としないdom操作のみを行う場合。新しいスコープはありません。(例えばngShowngMouseHoverngClickngRepeat

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

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


複数のコンポーネントが相互に通信する必要がある場合は、それらのスコープを分離して使用できるrequireため、ディレクティブを分離したままにすることができます。それで、それは可能性をどのように制限しますか?さらに、ディレクティブをより具体的にします(依存するものを宣言します)。したがって、1つのルールのみを残します。ディレクティブに状態があるか、それが使用されるスコープのデータが必要な場合は、分離されたスコープを使用します。それ以外の場合は、スコープを使用しないでください。そして、「子スコープ」について-私はかなり多くのディレクティブも書いており、この機能を必要としませんでした。「親スコープの一部の要素を変更する必要がある場合」-バインディングを使用します。
Valentyn Shybanov 2013

また、「親スコープの一部の要素を変更する必要性」について-子スコープの何かを変更した場合、変更は親スコープに入力されません(ダーティ$parentハックを使用しない限り)。以下のように-だから、実際のディレクティブのためにはかなり後ろに使用する必要があるように見えますが、その何かである「子供はスコープ」ngRepeatという繰り返しに各項目の新しい子スコープを作成します(それはまた、それを使用して作成scope.$new();していないscope: true
Valentyn Shybanov

1
同じ要素内で複数の分離されたスコープを要求することはできません。明示的にバインドしない限り、親スコープ内の関数にアクセスできません。(幸運ngClickなどを使用して)要求することは、私が同意する一種のデカップリングを作成しますが、それでも親ディレクティブに注意する必要があります。それがコンポーネントのようなものでない限り、私は分離に行くことに反対です。ディレクティブ(少なくとも、それらのほとんど)は、再利用性が高く、分離によってこれを壊します。
UmurKontacı2013

また、子スコープのinディレクティブも使用していませんが、子スコープは典型的には親スコープから継承するため、親スコープのプロパティ内のプロパティへのアクセスがあると、変更が反映されます。Angularの作者はMTVミートアップでそれについて話しました、「どこかにドットがあるの良いことですyoutube.com/watch?v=ZhfUv0spHCY
UmurKontacı13年

5
まず、分離スコープでは少し厳しすぎると思います。それらはあなたが彼らが持っていることを認めるよりも広い適用範囲を持っていると私はそれらを使用するときにあなたが(正しく)指摘した多くの課題を回避する方法があると思います。また、「エンド開発者にとってあまりカスタマイズしない」には同意しません。詳細については、私の回答を参照してください。そうは言っても、あなたの答えは悪くも悪くもなく、それは問題を解決したので、なぜそれが反対票を投じられたのかわかりません。だから、+ 1。
Josh David Miller

9

私の現在の理解と、それが他のJSの概念とどのように関連しているかを追加したいと思いました。

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

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

範囲:{}

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

スコープ:子

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


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

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

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


2

私はウムールに同意します。理論的には、分離されたスコープは素晴らしく「移植性がある」ように聞こえますが、自明ではない機能を含むようにアプリを構築する際に、完全に記述するためにいくつかのディレクティブ(他のディレクティブのネストまたは属性の追加)を組み込む必要に出会いました。ドメイン固有言語の目的である独自のHTML。

結局のところ、ディレクティブの各DOM呼び出しで複数の属性を使用してチェーンのすべてのグローバル値または共有値を渡す必要があるのはあまりにも奇妙です(分離スコープで必要です)。これらすべてをDOMに繰り返し書き込むのは馬鹿げているように見えるだけで、たとえこれらが共有オブジェクトであっても、効率が悪いと感じます。また、ディレクティブ宣言を不必要に複雑にします。$ parentを使用してディレクティブHTMLから値を「到達」して取得する回避策は、非常に悪いフォームのようです。

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

このようなことが起こる前に何十年もの間、ドメイン固有言語の夢を夢見てきたので、AngularJSがこのオプションを提供していることをうれしく思います。この分野でより多くの開発者が作業するにつれて、また、アーキテクトが記述、拡張、デバッグするのも簡単です。

-D

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.