Связывание входных данных с массивом примитивов с использованием ngRepeat => неизменяемых входов
Ниже приведена демонстрация моей проблемы.
$scope.myNumbers = [10, 20, 30];
<div ng-repeat="num in myNumbers">
<input type="text" ng-model="num">
<div>current scope: {{num}}</div>
</div>
Может ли кто-нибудь объяснить мне, почему входные данные являются неотредактированными /readonly? Если это по дизайну, какое обоснование?
ОБНОВЛЕНИЕ 2/20/2014
Похоже, это уже не проблема для v1.2.0 + Demo. Но имейте в виду, что, хотя пользовательские элементы управления теперь доступны для редактирования с новыми версиями angularJS, это свойство num
в дочерних областях, а не в родительской области, которые изменяются. Другими словами, изменение значений в пользовательских элементах управления не влияет на массив myNumbers
.
Ответы
Ответ 1
Может ли кто-нибудь объяснить мне, почему входные данные являются неотредактированными /readonly? Если это по дизайну, какое обоснование?
Это по дизайну от Angular 1.0.3. У Артема есть очень хорошее объяснение того, как 1.0.3+ работает, когда вы "привязываетесь к каждому элементу ng-repeat напрямую" – т.е.
<div ng-repeat="num in myNumbers">
<input type="text" ng-model="num">
Когда ваша страница изначально отображается, вот изображение ваших областей (я удалил один из элементов массива, поэтому на картинке будет меньше коробок):
(нажмите, чтобы увеличить)
Пунктирные линии показывают прототипическое наследование области.
Серые линии показывают child → родительские отношения (т.е. какие ссылки $parent
).
Коричневые линии показывают $$ nextSibling.
Серые ящики - это примитивные значения.
Синие квадратики - это массивы. Фиолетовые объекты.
Обратите внимание, что ответ SO, который вы указали в комментарии, был написан до выхода 1.0.3. До 1.0.3 значения num
в дочерних областях ngRepeat фактически изменились бы при вводе в текстовые поля. (Эти значения не будут видны в родительской области.) Начиная с версии 1.0, ngRepeat теперь заменяет значения ngRepeat scope num
значениями (неизмененными) из массива scope/mainCtrl scope myNumbers
во время цикла дайджеста. Это по сути делает исходные данные неизменными.
Исправлено использование массива объектов в вашем MainCtrl:
$scope.myNumbers = [ {value: 10}, {value: 20} ];
а затем привязывается к свойству value
объекта в ngRepeat:
<div ng-repeat="num in myNumbers">
<input type="text" ng-model="num.value">
<div>current scope: {{num.value}}</div>
Ответ 2
Эта проблема теперь рассматривается более поздними версиями AngularJS с функцией track by
, позволяющей повторителям по примитивам:
<div ng-repeat="num in myNumbers track by $index">
<input type="text" ng-model="myNumbers[$index]">
</div>
Страница не будет перерисовываться после каждого нажатия клавиши, что решает проблему потерянного фокуса. Официальный документ AngularJS довольно смутно и смущает об этом.
Ответ 3
ngRepeat использует ссылку на исходный массив. Поскольку integer (Number in js)
- это тип значения, а не ссылочный тип, поэтому не может быть передан по ссылке в javascript. Это изменение не будет распространено.
Вот демонстрация:
var x = 10;
var ox = {value:10};
var y = x;
var oy = ox;
y = 15
oy.value = 15;
Какими будут значения x
и ox
?
>> x = 10;
>> y = 15;
>> ox = {value:15};
>> oy = {value:15};
Все объекты javascript передаются по ссылке, и все примитивы передаются значением [ "string", "number" и т.д.].
Рабочий плункер http://plnkr.co/edit/7uG2IvAdC2sAEHbdHG58
Ответ 4
Кажется, что Angular не может написать модель, определенную таким образом. Используйте ссылку на начальный атрибут $scope, чтобы он правильно привязывал значение:
<div ng-repeat="num in myNumbers">
<input type="text" ng-model="myNumbers[$index]">
</div>
Ответ 5
У меня была аналогичная проблема (а также была нужна функция "добавить" и "удалить" ) и решила проблему следующим образом:
$scope.topics = [''];
$scope.removeTopic = function(i) {
$scope.topics.splice(i, 1);
}
<div ng-repeat="s in topics track by $index">
<input ng-model="$parent.topics[$index]" type="text">
<a ng-click="removeTopic($index)">Remove</a>
</div>
<a ng-click="topics.push('new topic')">Add</a>