web-dev-qa-db-ja.com

AngularJSのng-repeatスコープを持つディレクティブ分離スコープ

私は分離スコープを持つディレクティブを持っているので(他の場所でディレクティブを再利用できるように)、このディレクティブをng-repeatで使用すると動作しません。

このトピックに関するすべてのドキュメントとStack Overflowの回答を読み、問題を理解しました。私はすべての通常の落とし穴を避けてきたと信じています。

したがって、ng-repeatディレクティブによって作成されたスコープが原因でコードが失敗することを理解しています。私自身のディレクティブは分離スコープを作成し、親スコープ内のオブジェクトに双方向のデータバインディングを行います。私のディレクティブは、このバインドされた変数に新しいオブジェクト値を割り当てます。これは、ng-repeatなしでディレクティブを使用すると完全に機能します(親変数が正しく更新されます)。ただし、ng-repeatを使用すると、割り当てによりng-repeatスコープに新しい変数が作成され、親変数には変更が反映されません。これはすべて、私が読んだものに基づいて予想どおりです。

また、特定の要素に複数のディレクティブがある場合、1つのスコープのみが作成されることも読みました。また、各ディレクティブにpriorityを設定して、ディレクティブが適用される順序を定義できます。ディレクティブは優先度順にソートされ、コンパイル関数が呼び出されます( http://docs.angularjs.org/guide/directive でWordの優先度を検索します)。

したがって、優先順位を使用して、ディレクティブが最初に実行され、分離スコープを作成することを確認し、ng-repeatが実行されると、プロトタイプを継承するスコープを作成するのではなく、分離スコープを再使用できることを望んでいました親スコープから。 ng-repeatドキュメントには、そのディレクティブが優先レベル1000で実行されることが記載されています。 1が高い優先度レベルであるか低い優先度レベルであるかは明確ではありません。ディレクティブで優先レベル1を使用しても違いはありませんでしたので、2000を試しました。しかし、それは事態を悪化させます。双方向バインディングはundefinedになり、私のディレクティブは何も表示しません。

問題を表示するフィドル を作成しました。ディレクティブのpriority設定をコメントアウトしました。名前オブジェクトのリストと、名前オブジェクトの姓と名のフィールドを表示するname-rowというディレクティブがあります。表示された名前をクリックすると、メインスコープにselected変数が設定されます。名前の配列であるselected変数は、双方向データバインディングを使用してname-rowディレクティブに渡されます。

メインスコープで関数を呼び出すことで、これを機能させる方法を知っています。また、selectedが別のオブジェクトの内側にあり、外側のオブジェクトにバインドすると、動作することもわかっています。しかし、現時点ではこれらのソリューションには興味がありません。

代わりに、私が持っている質問は次のとおりです。

  • ng-repeatが親スコープからプロトタイプを継承するスコープを作成するのを防ぎ、代わりにディレクティブの分離スコープを使用させるにはどうすればよいですか?
  • ディレクティブの優先レベル2000が機能しないのはなぜですか?
  • Batarangを使用して、どのタイプのスコープが使用されているかを知ることは可能ですか?
95
Deepak Nulu

さて、上記の多くのコメントを通して、私は混乱を発見しました。最初に、明確化のいくつかのポイント:

  • ngRepeatは、選択した分離スコープに影響しません
  • ディレクティブの属性で使用するためにngRepeatに渡されるパラメーターdoは、プロトタイプで継承されたスコープを使用します
  • ディレクティブが機能しない理由は、分離スコープとは関係ありません

以下は同じコードの例ですが、ディレクティブが削除されています。

<li ng-repeat="name in names"
    ng-class="{ active: $index == selected }"
    ng-click="selected = $index">
    {{$index}}: {{name.first}} {{name.last}}
</li>

JSFiddle は、機能しないことを示しています。ディレクティブとまったく同じ結果が得られます。

なぜ機能しないのですか? AngularJSのスコープはプロトタイプ継承を使用するためです。親スコープの値selectedは、primitiveです。 JavaScriptでは、これは、子が同じ値を設定すると上書きされることを意味します。 AngularJSスコープにはゴールデンルールがあります。モデル値にはalways.が含まれている必要があります。つまり、決してプリミティブであってはなりません。詳細については、 this SO answer を参照してください。


以下は、スコープが最初にどのように見えるかの写真です。

enter image description here

最初のアイテムをクリックすると、スコープは次のようになります。

enter image description here

新しいselectedプロパティがngRepeatスコープに作成されたことに注意してください。コントローラースコープ003は変更されませんでした。

2番目のアイテムをクリックすると、おそらく何が起こるか推測できます。

enter image description here


したがって、実際にはngRepeatによって問題が発生することはありません。AngularJSの黄金律を破ることが原因です。修正する方法は、オブジェクトプロパティを使用することです。

$scope.state = { selected: undefined };
<li ng-repeat="name in names"
    ng-class="{ active: $index == state.selected }"
    ng-click="state.selected = $index">
    {{$index}}: {{name.first}} {{name.last}}
</li>

2番目のJSFiddle は、これも機能することを示しています。

最初は、スコープは次のようになります。

enter image description here

最初のアイテムをクリックした後:

enter image description here

ここでは、必要に応じてコントローラーのスコープが影響を受けています。

また、これが分離スコープのディレクティブでまだ機能することを証明するために(これもあなたの問題とは関係がないため)、ここに JSFiddle があります。オブジェクト。必要な変更は、primitiveの代わりにobjectを使用することだけであったことに注意してください。

最初のスコープ:

enter image description here

最初のアイテムをクリックした後のスコープ:

enter image description here

結論として、問題は分離スコープではなく、ngRepeatの動作方法でもありません。あなたの問題は、あなたがこのまさに問題につながることが知られている規則を破っていることです。 AngularJSのモデルには、常に.が必要です。

203

質問への回答を避けようとせずに、代わりに次のフィドルを見てください。

http://jsfiddle.net/dVPLM/

重要な点は、Angularの従来の動作と戦い、変更しようとする代わりに、ng-repeatで動作するようにディレクティブを構造化して、オーバーライドするのではないということです。

テンプレートで:

    <name-row 
        in-names-list="names"
        io-selected="selected">
    </name-row>

あなたのディレクティブで:

    template:
'        <ul>' +      
'            <li ng-repeat="name in inNamesList" ng-class="activeClass($index)" >' +
'                <a ng-click="setSelected($index)">' +
'                    {{$index}} - {{name.first}} {{name.last}}' +
'                </a>' +
'            </li>' +
'        </ul>'

あなたの質問に答えて:

  • ng-repeatはスコープを作成しますが、これを変更しようとしてはいけません。
  • ディレクティブの優先順位は単なる実行順序ではありません-参照: AngularJS:HTMLコンパイラはどのようにコンパイルの順序を調整しますか?
  • Batarangでは、パフォーマンスタブをチェックすると、各スコープにバインドされた式を確認し、これが期待に一致するかどうかを確認できます。
6
Alex Osborn