1. ホーム
  2. angularjs

[解決済み] AngularJSのng-repeatスコープを使ったディレクティブアイソレートスコープ

2023-02-05 22:21:55

質問

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

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

で作成されたスコープが原因で私のコードが失敗することを理解しました。 ng-repeat ディレクティブによって作られるスコープが原因で失敗するのだと理解しています。私のディレクティブは孤立したスコープを作り、親スコープにあるオブジェクトに双方向のデータバインディングを行います。私のディレクティブはこのバインドされた変数に新しいオブジェクトの値を割り当てます。 ng-repeat を使用しない場合、これは完全に動作します (親変数は正しく更新されます)。しかし ng-repeat では、代入は新しい変数を作成します。 ng-repeat スコープに新しい変数が作成され、親変数はその変更を認識しません。これはすべて、私が読んだことに基づくと予想されることです。

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

そこで、優先順位を使って、私のディレクティブが最初に実行され、isolate-scope を作成することになるようにできればと思ったのですが、そのときに ng-repeat が実行されたとき、プロトタイプ的に親スコープを継承するスコープを作成する代わりに、孤立したスコープを再利用します。そのため ng-repeat のドキュメントによると、このディレクティブは優先度 1000 . しかし 1 が高い優先度なのか低い優先度なのかが不明です。優先レベルを使用した場合 1 を使っても違いが出なかったので、試しに 2000 . しかし、これは事態を悪化させます。私の双方向バインディングは undefined になり、ディレクティブは何も表示されません。

私は を作成しました。 . をコメントアウトしました。 priority をコメントアウトしています。私は名前オブジェクトのリストと name-row というディレクティブがあり、name オブジェクトの姓と名のフィールドを表示しています。表示された名前がクリックされたとき、その名前に対して selected 変数をセットするようにしたい。名前の配列である selected 変数に渡されます。 name-row ディレクティブに渡されます。

私は、メインスコープで関数を呼び出すことによって、これを動作させる方法を知っています。また、もし selected が別のオブジェクトの内部にあり、外側のオブジェクトにバインドすれば、うまくいくことも知っています。しかし、私は今のところ、それらの解決策に興味がありません。

その代わりに、私が持っている疑問は

  • どのようにすれば ng-repeat が親スコープをプロトタイプ的に継承するスコープを作るのを防ぎ、 代わりに私のディレクティブの isolate-scope を使うようにするにはどうしたらよいでしょうか?
  • なぜ優先順位が 2000 が機能しないのでしょうか?
  • Batarangを使用して、使用中のスコープの種類を知ることは可能ですか?

どのように解決するのですか?

さて、上記の多くのコメントを通じて、私は混乱を発見しました。まず、いくつかの点を明確にします。

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

同じコードで、ディレクティブを削除した例です。

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

ここでは JSFiddle で、これが動作しないことを示します。あなたのディレクティブと全く同じ結果を得ることができます。

なぜうまくいかないのでしょうか?AngularJSのスコープはプロトタイプの継承を使用しているからです。値 selected を親スコープに置くと プリミティブ . JavaScriptでは、子プロセスが同じ値を設定すると上書きされることを意味します。AngularJSのスコープには黄金律があります:モデルの値は 常に を持つことです。 . を含む。つまり、これらは決してプリミティブであってはならないのです。参照 このSOの答え を参照してください。


スコープが初期状態でどのようなものかを示す画像です。

最初の項目をクリックすると、スコープが次のようになりました。

新しい 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>

ここでは 第二のJSFiddle で、これも動作することがわかります。

初期状態では以下のようなスコープになっています。

最初の項目をクリックした後

ここでは、希望通り、コントローラスコープに影響を及ぼしています。

また、これが分離されたスコープを持つディレクティブでも動作することを証明するために (繰り返しますが、これはあなたの問題とは関係ありませんから)、 ここでは JSFiddle をご覧ください。これも、ビューにオブジェクトが反映されていなければなりません。必要な唯一の変更は オブジェクトを使用することです。 の代わりに プリミティブ .

最初はスコープ。

最初の項目をクリックした後のスコープ。

結論から言うと、もう一度言いますが、あなたの問題はisolateスコープやngRepeatの動作方法に関するものではありません。あなたの問題は、まさにこの問題を引き起こすことが知られているルールを破っていることです。AngularJSのモデルには、常に . .