Складність з 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.