ng-model、ng-repeat、およびinputの難しさ


116

ngRepeatおよびを使用して、ユーザーがアイテムのリストを編集できるようにしようとしていますngModel。(このフィドルを参照してください。)ただし、私が試した両方のアプローチは奇妙な動作を引き起こします。1つはモデルを更新せず、もう1つは各キーダウンでフォームをぼかします。

ここで何か悪いことをしていますか?これはサポートされているユースケースではありませんか?

フィドルのコードを以下に示します。

<html ng-app>
    <head>
        <link href="//netdna.bootstrapcdn.com/twitter-bootstrap/2.2.1/css/bootstrap-combined.min.css" rel="stylesheet">
    </head>
    <body ng-init="names = ['Sam', 'Harry', 'Sally']">
        <h1>Fun with Fields and ngModel</h1>
        <p>names: {{names}}</p>
        <h3>Binding to each element directly:</h3>
        <div ng-repeat="name in names">
            Value: {{name}}
            <input ng-model="name">                         
        </div>
        <p class="muted">The binding does not appear to be working: the value in the model is not changed.</p>
        <h3>Indexing into the array:</h3>
        <div ng-repeat="name in names">
            Value: {{names[$index]}}
            <input ng-model="names[$index]">                         
        </div>
        <p class="muted">Type one character, and the input field loses focus. However, the binding appears to be working correctly.</p>
    </body>
</html>


1
これはstackoverflow.com/questions/12977894/…とよく似ていますが、2番目のアプローチはここで新しくなったため、完全に重複しているわけではありません。そこで、詳細な回答(Artemと同様)を提供し、ng-repeat子スコープがどのように機能するかを説明しました。
Mark Rajcok

最終的にこのスレッドを見つける前に妥当な量のグーグルがかかるため、「ngRepeat入力から更新されていない親モデル」または「ngRepeatを使用するときに更新されていないモデル」または「(親)にバインドされていないngRepeat入力」のように名前を変更しますか?モデル」?多分あなたはタイトルのためのより良いアイデアを持っていますか?
vucalur 2013年

回答:


120

これは拘束力のある問題のようです。

アドバイスはプリミティブにバインドしないことです。

あなたはngRepeatそれがオブジェクトを反復しなければならないとき、コレクション内の文字列を反復処理されます。問題を解決するには

<body ng-init="models = [{name:'Sam'},{name:'Harry'},{name:'Sally'}]">
    <h1>Fun with Fields and ngModel</h1>
    <p>names: {{models}}</p>
    <h3>Binding to each element directly:</h3>
    <div ng-repeat="model in models">
        Value: {{model.name}}
        <input ng-model="model.name">                         
    </div>

jsfiddle:http ://jsfiddle.net/jaimem/rnw3u/5/


3
最初のハイパーリンクをありがとう、ぼかし/フォーカスの喪失/フリッカーの問題について説明しています。なぜそうなったのかといつも思っていました。
Mark Rajcok

2
そのおかげで、たくさん助けました。それでも、プリミティブへのバインディングはそこにあるはずです...
Daniel Gruszczyk 2014

1
古い投稿ですがthnxは、「ngRepeat内のモデルを変更しない」問題を見つけるのに少し時間を要し、プリミティブにバインドしないようにアドバイスしました
Stefanos Chrs

また、入力中にリスト全体を変更しないでください-私が陥った罠。コレクションの変更を監視し、それを同一のコピーで置き換えていました。そのため、プリミティブにバインドしていなくても、要素は再作成されていました。
z0r 2016

71

Angularの最新バージョン(1.2.1)を使用して追跡する $indexます。この問題は修正されました

http://jsfiddle.net/rnw3u/53/

<div ng-repeat="(i, name) in names track by $index">
    Value: {{name}}
    <input ng-model="names[i]">                         
</div>

仕事をしました。これ以上の参照損失はありません
Dan Ochiana

私はそのために長い間検索を行いました...とても簡単です... これを答えとしてください!
Andrea

oooooh、ng-repeatに「track by $ index」を追加すると、「ちらつき」の問題も修正されます
boi_echos

43

NgModelControllerスコープngRepeatngModelをどのように理解する必要がある場合、困難な状況に陥ります仕事を。また、1.0.3バージョンを使用してみてください。あなたの例は少し異なる動作をします。

jm-が提供するソリューションを簡単に使用できます

しかし、状況をもっと深く扱いたい場合は、次のことを理解する必要があります。

  • AngularJSのしくみ ;
  • スコープには階層構造があります。
  • ngRepeatはすべての要素に新しいスコープを作成します。
  • ngRepeatは、追加情報を含むアイテムのキャッシュを構築します(hashKey); (キャッシュ内にない)すべての新しいアイテムの各ウォッチ呼び出しで、ngRepeatは新しいスコープ、DOM要素などを構築します。詳細な説明
  • 1.0.3からngModelControllerは、実際のモデル値で入力を再レンダリングします。

AngularJS 1.0.3での「各要素への直接バインド」の例の動作:

  • 入力に文字'f'を入力します。
  • ngModelController=>アイテムスコープのためのモデルを変更する(名前の配列が変更されていません)name == 'Samf'names == ['Sam', 'Harry', 'Sally']
  • $digest ループが開始されます。
  • ngRepeatアイテムのスコープ('Samf')のモデル値を、変更されていない名前の配列('Sam')の値で置き換えます。
  • ngModelController入力を実際のモデル値で再レンダリングします('Sam')。

「配列へのインデックス作成」の例がどのように機能するか:

  • 入力に文字'f'を入力します。
  • ngModelController名前の変更項目array=> `names == ['Samf'、 'Harry'、 'Sally'];
  • $ digestループが開始されます。
  • ngRepeat 見つけられない 'Samf'キャッシュに。
  • ngRepeat 新しいスコープを作成し、新しい入力を持つ新しいdiv要素を追加します(そのため、入力フィールドはフォーカスを失います-古い入力を持つ古いdivは新しい入力を持つ新しいdivに置き換えられます);
  • 新しいDOM要素の新しい値がレンダリングされます。

また、AngularJS Batarangを使用して、入力したdivのスコープの$ idがどのように変化するかを確認することもできます。


1.0.3の説明をありがとう。1.0.3では、「直接バインド」の場合、入力フィールドが壊れているように見えます(少なくともChromeでは)変更や入力された入力を受け付けていないように見えるのは興味深いことです。近い将来、これについてかなりの数のSO投稿が表示されると確信しています:)何かが間違っていることがより明らかになるため、この新しい方法の方が良いと思います。
Mark Rajcok

6

すべてのキーストロークでモデルを更新する必要がない場合は、バインドしてname、blurイベントで配列項目を更新します。

<div ng-repeat="name in names">
    Value: {{name}}
    <input ng-model="name" ng-blur="names[$index] = name" />
</div>

なぜ使用していないng-model="names[$index]"...私はそれが回避策を知っているが、それは動作します;-)
DraškoKokić


2

問題は、オブジェクトがどのようにng-model機能しinputnameオブジェクトを上書きして、で失われるかの方法にあるようですng-repeat

回避策として、次のコードを使用できます。

<div ng-repeat="name in names">
    Value: {{name}}
    <input ng-model="names[$index]">                         
</div>

それが役に立てば幸い


1

私はそれが魅力のように働いた私の問題のために上記の解決策を試しました。ありがとう!

http://jsfiddle.net/leighboone/wn9Ym/7/

これが私のバージョンです:

var myApp = angular.module('myApp', []);
function MyCtrl($scope) {
    $scope.models = [{
        name: 'Device1',
        checked: true
    }, {
        name: 'Device1',
        checked: true
    }, {
        name: 'Device1',
        checked: true
    }];

}

そして私のHTML

<div ng-app="myApp">
    <div ng-controller="MyCtrl">
         <h1>Fun with Fields and ngModel</h1>
        <p>names: {{models}}</p>
        <table class="table table-striped">
            <thead>
                <tr>
                    <th></th>
                    <th>Feature 1</td>
                    <th>Feature 2</th>
                    <th>Feature 3</th>
                </tr>
            </thead>
            <tbody>
                <tr>
                    <td>Device</td>
                   <td ng-repeat="modelCheck in models" class=""> <span>
                                    {{modelCheck.checked}}
                                </span>

                    </td>
                </tr>
                <tr>
                    <td>
                        <label class="control-label">Which devices?</label>
                    </td>
                    <td ng-repeat="model in models">{{model.name}}
                        <input type="checkbox" class="checkbox inline" ng-model="model.checked" />
                    </td>
                </tr>
            </tbody>
        </table>
    </div>
</div>

-5

どのようにして:

<select ng-model="myModel($index+1)">

そして私のインスペクター要素では:

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