Что такое angularjs способ для сбора данных со многих входов?
Я изучаю angularjs и хочу позволить пользователю вводить много входов. Когда эти входы вводятся list
элементы массива должны измениться соответственно. Я хотел попробовать использовать директиву ngRepeat, но я прочитал, что, поскольку она создает новую область, я не могу связать данные:
<div ng-repeat="item in list">
<label>Input {{$index+1}}:</label>
<input ng-model="item" type="text"/>
</div>
Мне было интересно, стоит ли мне использовать пользовательскую директиву для этого или подходить к ней иначе.
5 ответов
Вам повезет больше, если ваш list
является массивом объектов (в отличие от массива примитивов). Это прекрасно работает, хотя новая область создается с ng-repeat
:
<div ng-repeat="item in list">
<label>Input {{$index+1}}:</label>
<input ng-model="item.value" type="text"/>
</div>
с контроллером:
function TestController($scope) {
$scope.list = [ { value: 'value 1' }, { value: 'value 2' }, { value: 'value 3' } ];
}
Посмотрите эту скрипку в качестве примера.
С другой стороны, если вы пытаетесь привязать массив строк, новая область действия вызовет проблему, так как изменяемые вами значения не будут привязаны к исходным примитивам строк массива (как в этом примере скрипта).
Причина, по которой привязка данных к примитивному "элементу" не работает, заключается в том, что ng-repeat создает дочерние области для каждого элемента. Для каждого элемента ng-repeat имеет новую дочернюю область, прототипически наследуемую от родительской области (см. Пунктирные линии на рисунке ниже), а затем назначает значение элемента новому свойству в дочерней области (красные элементы на рисунке ниже). Имя нового свойства - это имя переменной цикла. Из исходного кода ng-repeat:
childScope = scope.$new();
...
childScope[valueIdent] = value;
Если элемент является примитивом, новому дочернему свойству области действия по существу присваивается копия значения примитива. Это свойство дочерней области невидимо для родительской области, и изменения, которые вы вносите в поле ввода, сохраняются в этом свойстве дочерней области. Например, предположим, что мы имеем в родительской области
$scope.list = [ 'value 1', 'value 2', 'value 3' ];
И в HTML:
<div ng-repeat="item in list">
Тогда первая дочерняя область будет иметь следующий item
свойство, с примитивным значением (value 1
):
item: "value 1"
Из-за привязки данных модели ng изменения, вносимые вами в поле ввода формы, сохраняются в этом дочернем свойстве области действия.
Вы можете убедиться в этом, зарегистрировав дочернюю область на консоли. Добавьте к своему HTML внутри ng-repeat:
<a ng-click="showScope($event)">show scope</a>
Добавьте к своему контроллеру:
$scope.showScope = function(e) {
console.log(angular.element(e.srcElement).scope());
}
С подходом @Gloopy каждый дочерний контекст по-прежнему получает новое свойство "item", но поскольку список теперь является массивом объектов,
childScope[valueIdent] = value;
приводит к тому, что значение свойства элемента устанавливается на ссылку на один из объектов массива (не на копию).Используя метод showScope(), вы увидите, что дочерняя область item
Значение свойства ссылается на один из объектов массива - оно больше не является примитивным значением.
Смотрите также , не связывайте с примитивами в ng-repeat дочерних областях и
Каковы нюансы объема прототипного / прототипического наследования в AngularJS? (который содержит изображения того, как выглядят области при использовании ng-repeat).
Я нашел интересный способ сделать это, и это позволяет мне работать с массивом примитивов.
Я использую AngularJS 1.2.1, единственную версию, в которой я могу сделать эту работу.
HTML:
<div ng-repeat="item in list">
<label>Input {{$index+1}}:</label>
<input ng-model="item" type="text" ng-blur="editItem($index, item)"/>
</div>
JavaScript:
$scope.editItem = function(idx, eItem) {
$scope.list[idx] = eItem;
};
Ссылка: http://jsfiddle.net/bxD2P/10/ (спасибо Gloopy за стартовую скрипку)
Я уверен, что есть простые способы пробить дыры в том, как это работает, и я хотел бы услышать их. Это позволило бы мне укрепить мой код.
Рассмотрите возможность использования ngList
директива https://docs.angularjs.org/api/ng/directive/ngList
Вот способ сделать это. Я использовал текстовые зоны и структуру, отличную от моих репитеров, но основная концепция:
- отобразить простое значение на основе индекса. (не связан)
- на размытие обновить модель
- при обновлении модели повторно сделать
Это, по сути, притворная привязка.
Рабочая скрипка - http://jsfiddle.net/VvnWY/4/
HTML:
<script type="text/ng-template" id="textareas.html">
<textarea ng-if="strings" ng-repeat="str in strings" ng-blur="blur( $event, $index )">{{strings[$index]}}</textarea>
</script>
<div ng-controller="MyCtrl">
Here's a few strings: <br />
<div ng-repeat="str in strings">{{strings[$index]}}</div>
Here's the strings as editable (twice so that you can see the updates from a model change): <br />
<form-textareas strings="strings"></form-textareas>
<form-textareas strings="strings"></form-textareas>
</div>
JS:
var myApp = angular.module('myApp',[]);
angular.module('myApp', [])
.controller('MyCtrl', ['$scope', function($scope) {
$scope.strings = [ "foo", "bar", "cow" ];
}])
.directive('formTextareas', function() {
return {
restrict: "E",
scope: {
strings: '='
},
templateUrl: "textareas.html",
link: function( $scope ){
$scope.blur = function( $event, $index ){
$scope.strings[ $index ] = $event.currentTarget.value;
};
}
};
})
;