Что такое 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;
            };
        }
    };
  })
;
Другие вопросы по тегам