Kesulitan dengan ng-model, ng-repeat, dan input

116

Saya mencoba mengizinkan pengguna untuk mengedit daftar item dengan menggunakan ngRepeatdan ngModel. ( Lihat biola ini .) Namun, kedua pendekatan yang saya coba menyebabkan perilaku aneh: satu tidak memperbarui model, dan yang lain mengaburkan formulir pada setiap tuts bawah.

Apakah saya melakukan sesuatu yang salah di sini? Apakah ini bukan kasus penggunaan yang didukung?

Ini kode dari biola, disalin untuk kenyamanan:

<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>

</s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> orang </s>

Nick Heiner
sumber
1
Ini sangat mirip dengan stackoverflow.com/questions/12977894/… , tetapi pendekatan ke-2 baru di sini, jadi ini bukan duplikat. Saya memberikan jawaban terperinci (mirip dengan Artem) di sana, menjelaskan cara kerja cakupan anak ng-repeat.
Mark Rajcok
Karena saya harus melakukan googling dalam jumlah yang wajar sebelum akhirnya menemukan utas ini, bolehkah saya mengganti namanya menjadi sth seperti: "Model Induk tidak diperbarui dari input ngRepeat" atau "Model tidak diperbarui saat menggunakan ngRepeat" atau "input ngRepeat tidak terikat ke (induk) model"? Mungkin Anda punya ide yang lebih baik untuk judulnya?
vucalur

Jawaban:

120

Ini tampaknya menjadi masalah yang mengikat.

Nasihatnya jangan terikat pada primitif .

Anda melakukan ngRepeatiterasi terhadap string di dalam koleksi, saat itu harus melakukan iterasi terhadap objek. Untuk memperbaiki masalah Anda

<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/

jaime
sumber
3
Terima kasih untuk hyperlink pertama, karena ini menjelaskan masalah blur / kehilangan fokus / flicker. Saya selalu bertanya-tanya mengapa itu terjadi.
Mark Rajcok
2
terima kasih untuk itu, banyak membantu. Namun, mengikat primitif harus ada di ...
Daniel Gruszczyk
1
posting lama tapi thnx, butuh beberapa waktu untuk menemukan masalah 'tidak mengubah model di dalam ngRepeat' dan itu adalah saran Anda untuk tidak mengikat ke primitif
Stefanos Chrs
Juga: Jangan mengubah seluruh daftar saat mengetik - saya terjebak dalam jebakan. Saya mengamati koleksi untuk perubahan, dan menggantinya dengan salinan identik - jadi meskipun saya tidak mengikat primitif, elemen sedang dibuat ulang.
z0r
71

Menggunakan Angular versi terbaru (1.2.1) dan lacak oleh $index. Masalah ini telah diperbaiki

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

<div ng-repeat="(i, name) in names track by $index">
    Value: {{name}}
    <input ng-model="names[i]">                         
</div>
Thilaga
sumber
melakukan pekerjaan itu; tidak ada lagi kehilangan referensi
Dan Ochiana
Aku mencari begitu lama untuk itu .... begitu mudah ... begitu tersembunyi. Berikan ini sebagai jawabannya!
Andrea
oooooh, jadi menambahkan "track by $ index" di ng-repeat juga memperbaiki masalah "
flickering
43

Anda masuk ke situasi yang sulit ketika perlu untuk memahami cara kerja cakupan , ngRepeat dan ngModel dengan NgModelController . Coba juga gunakan versi 1.0.3. Teladan Anda akan bekerja sedikit berbeda.

Anda cukup menggunakan solusi yang disediakan oleh jm-

Tetapi jika Anda ingin menghadapi situasi tersebut lebih dalam, Anda harus memahami:

  • bagaimana AngularJS bekerja ;
  • lingkup memiliki struktur hierarki;
  • ngRepeat membuat ruang lingkup baru untuk setiap elemen;
  • ngRepeat build cache item dengan informasi tambahan (hashKey); pada setiap panggilan arloji untuk setiap item baru (yang tidak ada dalam cache) ngRepeat membangun cakupan baru, elemen DOM, dll . Deskripsi yang lebih mendetail .
  • dari 1.0.3 ngModelController merender input dengan nilai model aktual.

Bagaimana contoh Anda "Mengikat ke setiap elemen secara langsung" bekerja untuk AngularJS 1.0.3:

  • Anda memasukkan huruf 'f'ke masukan;
  • ngModelControllerperubahan model untuk lingkup item (nama array tidak berubah) => name == 'Samf', names == ['Sam', 'Harry', 'Sally'];
  • $digest loop dimulai;
  • ngRepeatmengganti nilai model dari item scope ( 'Samf') dengan nilai dari nama yang tidak berubah array ( 'Sam');
  • ngModelControllermerender masukan dengan nilai model aktual ( 'Sam').

Cara kerja contoh "Mengindeks ke dalam array":

  • Anda memasukkan huruf 'f'ke masukan;
  • ngModelControllermengubah item dalam names array=> `names == ['Samf', 'Harry', 'Sally'];
  • $ digest loop dimulai;
  • ngRepeattidak dapat ditemukan 'Samf'di cache;
  • ngRepeat membuat ruang lingkup baru, menambahkan elemen div baru dengan masukan baru (itulah sebabnya bidang masukan kehilangan fokus - div lama dengan masukan lama diganti dengan div baru dengan masukan baru);
  • nilai baru untuk elemen DOM baru dirender.

Anda juga dapat mencoba menggunakan AngularJS Batarang dan melihat bagaimana mengubah $ id dari ruang lingkup div dengan masukan yang Anda masukkan.

Artem Andreev
sumber
terima kasih atas penjelasan 1.0.3. Sangat menarik bahwa di 1.0.3, kasus "mengikat secara langsung", bidang masukan tampaknya rusak, dalam arti tidak tampak menerima perubahan / masukan yang diketik (setidaknya di Chrome). Saya yakin kita akan melihat beberapa posting SO tentang ini dalam waktu dekat :) Saya rasa cara baru ini lebih baik, karena akan lebih jelas bahwa ada sesuatu yang salah.
Mark Rajcok
6

Jika Anda tidak memerlukan model untuk memperbarui dengan setiap penekanan tombol, cukup ikat ke namedan kemudian perbarui item array pada peristiwa blur:

<div ng-repeat="name in names">
    Value: {{name}}
    <input ng-model="name" ng-blur="names[$index] = name" />
</div>
Bill Heitstuman
sumber
Mengapa tidak menggunakan ng-model="names[$index]"... Saya tahu ini solusinya, tetapi berhasil ;-)
Draško Kokić
2

Masalahnya tampaknya pada cara ng-modelbekerja dengan inputdan menimpa nameobjek, membuatnya hilangng-repeat .

Sebagai solusinya, seseorang dapat menggunakan kode berikut:

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

Semoga membantu

Draško Kokić
sumber
1

Saya mencoba solusi di atas untuk masalah saya itu berfungsi seperti pesona. Terima kasih!

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

Ini versi saya:

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

}

dan HTML saya

<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>
Leigh Lawhon
sumber
-5

bagaimana melakukan sesuatu seperti:

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

Dan dalam elemen inspektur saya menjadi:

<select ng-model="myModel1">
...
<select ng-model="myModel2">
Nicolás Oviedo
sumber