私は分離スコープを持つディレクティブを持っているので(他の場所でディレクティブを再利用できるように)、このディレクティブを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
が機能しないのはなぜですか?さて、上記の多くのコメントを通して、私は混乱を発見しました。最初に、明確化のいくつかのポイント:
以下は同じコードの例ですが、ディレクティブが削除されています。
<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 を参照してください。
以下は、スコープが最初にどのように見えるかの写真です。
最初のアイテムをクリックすると、スコープは次のようになります。
新しいselected
プロパティがngRepeatスコープに作成されたことに注意してください。コントローラースコープ003は変更されませんでした。
2番目のアイテムをクリックすると、おそらく何が起こるか推測できます。
したがって、実際には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 は、これも機能することを示しています。
最初は、スコープは次のようになります。
最初のアイテムをクリックした後:
ここでは、必要に応じてコントローラーのスコープが影響を受けています。
また、これが分離スコープのディレクティブでまだ機能することを証明するために(これもあなたの問題とは関係がないため)、ここに JSFiddle があります。オブジェクト。必要な変更は、primitiveの代わりにobjectを使用することだけであったことに注意してください。
最初のスコープ:
最初のアイテムをクリックした後のスコープ:
結論として、問題は分離スコープではなく、ngRepeatの動作方法でもありません。あなたの問題は、あなたがこのまさに問題につながることが知られている規則を破っていることです。 AngularJSのモデルには、常に.
が必要です。
質問への回答を避けようとせずに、代わりに次のフィドルを見てください。
重要な点は、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
はスコープを作成しますが、これを変更しようとしてはいけません。