Складність з ng-моделлю, ng-повтором та введеннями


116

Я намагаюся дозволити користувачеві редагувати список елементів за допомогою ngRepeatі ngModel. ( Дивіться цю загадку .) Однак обидва підходи, які я намагався, призводять до химерної поведінки: один не оновлює модель, а інший розмиває форму при кожному натисканні клавіш.

Чи я тут щось неправильно роблю? Це не підтримуваний випадок використання?

Ось код із скрипки, скопійований для зручності:

<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/… , але другий підхід тут новий, тому він не є точно дублікатом. Я дав детальну відповідь (подібну до Артема) там, пояснюючи, як працюють прилади для повторення дітей.
Марк Райкок

Оскільки це коштувало мені розумної кількості googling, перш ніж нарешті знайти цю тему, чи можу я перейменувати її на sth на зразок: "батьківська модель не оновлена ​​з вводу ngRepeat" або "Модель не оновлюється при використанні ngRepeat" або "ngRepeat вхідних даних не пов'язаний (батьківський) модель "? Можливо, у вас є кращі ідеї для звання?
вукалур

Відповіді:


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
Дякуємо за перше гіперпосилання, оскільки воно пояснює проблему розмиття / втрати фокусу / мерехтіння. Я завжди цікавився, чому так сталося.
Марк Райчкок

2
спасибі за це, допомогло багато. І все-таки прив'язка до примітивів повинна бути там ...
Даніель Грущчик

1
стара публікація, але thnx, знадобилося мені трохи часу, щоб знайти проблему "не змінюючи модель всередині ngRepeat", і це була ваша порада не прив'язуватися до примітивів
Stefanos Chrs

Також: Не змінюйте весь список під час набору тексту - в пастку, в яку я потрапив. Я спостерігав за колекцією за змінами і замінював її ідентичною копією - тому, хоч я і не прив’язувався до примітивів, елементи відтворювались.
z0r

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

Я так довго шукав це .... так легко ... просто так приховано. Дайте це як відповідь!
Андреа

oooooh, тому додавання "track by $ index" у ng-повторі також виправляє проблему "мерехтіння"
boi_echos

43

Ви потрапляєте у складну ситуацію, коли потрібно зрозуміти, як працюють області застосування , ngRepeat і ngModel з NgModelController . Спробуйте також використовувати версію 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'];
  • $ цикл дайджесту запускається;
  • ngRepeatне вдається знайти 'Samf'в кеші;
  • ngRepeat створює новий діапазон, додає новий елемент div з новим входом (саме тому поле введення втрачає фокус - старий div зі старим входом замінюється на новий div з новим входом);
  • відображаються нові значення для нових елементів DOM.

Крім того, ви можете спробувати використовувати AngularJS Batarang і побачити, як змінюється $ id сфери діва на вхід, в який ви вводите.


дякую за пояснення 1.0.3 Цікаво, що в 1.0.3, випадку "прив'язувати безпосередньо", поле введення видається порушеним, в тому сенсі, що воно, схоже, не приймає жодних змін / набраних даних (принаймні в Chrome). Я впевнений, що ми побачимо досить багато повідомлень про це найближчим часом :) Я вважаю, що цей новий спосіб є кращим, оскільки буде очевидніше, що щось не так.
Марк Райчкок

6

Якщо модель не потребує оновлення кожного натискання клавіші, просто прив’яжіть до нього, nameа потім оновіть елемент масиву для події розмивання:

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

Чому б не використовувати ng-model="names[$index]"... Я знаю, що це
обхід


2

Здається, проблема полягає в тому, як ng-modelпрацює inputі перезаписує nameоб'єкт, роблячи його втраченим 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">
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.