Angular ng-repeat с ng-form, доступ к проверке в контроллере

Я пытаюсь создать редактируемый список, используя ng-repeat. Я хочу напомнить пользователю обновить любые изменения, прежде чем двигаться дальше, поэтому я использую ng-form для создания «вложенных» форм на лету, потому что в документации говорится, что затем я могу использовать проверку для этих динамически созданных входных данных.

Хотя это, кажется, работает в HTML, я не вижу, как получить доступ к этим динамически созданным формам и связанным полям проверки в контроллере. В частности, когда пользователь изменяет ввод, я использую свойство формы $dirty, чтобы вызвать кнопку, чтобы сообщить пользователю о фиксации изменений. Все идет нормально. Однако, как только изменения будут зафиксированы, я хочу $setPristine() в поле указать, что изменения были установлены. Могут быть и другие способы гарантировать, что изменения фиксируются на каждом входе, прежде чем я разрешаю фиксацию основной формы, но это было лучшее, что я мог придумать.

К сожалению, несмотря на то, что в документации сказано, что если я назову ng-форму, она будет распространена на объект $scope, я не могу найти способ получить к ней доступ. $scope.dynamic_form не определено.

Вот плункер, показывающий, что я имею в виду:

plnk

Спасибо!

[EDIT] Просто чтобы добавить к проблеме, что работает для этого конкретного примера, так это добавить к ng-click на динамически созданный ввод:

ng-click="namesForm.name.$setPristine();clean()"

Но у меня все еще нет доступа к динамически созданной форме в контроллере. Я хотел бы, например, добавить наблюдателя к namesForm.name.$pristine, чтобы я мог установить mainForm.$setValidity(false) всякий раз, когда подчиненная форма $dirty, чтобы запретить пользователю отправлять основную форму до тех пор, пока все изменения подчиненной формы не будут зафиксированы.

Итак, в двух словах, проблема заключается в том, как получить доступ в родительском контроллере к значениям проверки динамически созданной вложенной формы ngForm?


person MyTimeFinder    schedule 03.06.2014    source источник


Ответы (1)


Обновлено 17 января 2015 г.:

Как указал Леблан Менезес в комментариях, Angular 1.3 теперь поддерживает интерполяцию с директивами form, ngForm и input.

Это означает, что использование выражений для именования ваших элементов:

<div ng-form="namesForm_{{$index}}" ng-repeat="name in names">
    <input type="text"
           name="input_{{$index}}_0"></input>
    <!-- ... -->
</div>  

будет работать как положено:

$scope['namesForm_0']
$scope.namesForm_1

// Access nested form elements:
$scope.namesForm_1.input_1_0
...

Исходный ответ для Angular ‹= 1.2:

Работа с формами и ngFormController может довольно быстро усложниться.

Вы должны знать, что вы можете динамически добавлять элементы формы и поля ввода, но они не могут быть динамически именованы — интерполяция не работает в директивах ngForm или name.

Например, если вы попытались назвать свои вложенные формы динамически следующим образом:

<div ng-form="namesForm_{{$index}}" ng-repeat="name in names">
    <!-- ... -->
</div>  

Вместо того, чтобы делать все вложенные формы доступными в области действия, например: scope['namesForm_0'], у вас будет доступ только к одной (последней) форме с литеральным именем scope['namesForm_{{$index}}'].

В вашей ситуации вам нужно создать пользовательскую директиву, которая будет добавлена ​​вместе с ngForm для обработки настроек $pristine$ и $invalid для этого экземпляра формы.

JavaScript:

Эта директива будет следить за состоянием $dirty своей формы, чтобы установить $validity для предотвращения отправки в случае загрязнения, и обрабатывать установку состояния $pristine при нажатии кнопки «очистить».

app.directive('formCleaner', function(){
    return {
        scope: true,
        require: '^form',
        link: function(scope, element, attr){
            scope.clean = function () {
                scope.namesForm.$setPristine();
            };

            scope.$watch('namesForm.$dirty', function(isDirty){
                scope.namesForm.$setValidity('name', !isDirty);
            });
        }
    };
});

HTML:

Тогда единственным изменением в вашем HTML будет добавление директивы formCleaner.

Поэтому измените исходный HTML-код следующим образом:

<body ng-controller="MainCtrl">
    <form name="mainForm" submit="submit()">
        <h3>My Editable List</h3>
        <div ng-form="namesForm"
             ng-repeat="name in names">
            <!-- ... -->
        </div>
        <button class="btn btn-default" type="submit">Submit</button>
    </form>
</body>

к этому, добавив form-cleaner рядом с ng-form:

<body ng-controller="MainCtrl">
    <form name="mainForm" submit="submit()">
        <h3>My Editable List</h3>

        <!-- Add the `form-cleaner` directive to the element with `ng-form` -->
        <div form-cleaner
             ng-form="namesForm"
             ng-repeat="name in names">
            <!-- ... -->
        </div>
        <button class="btn btn-default" type="submit">Submit</button>
    </form>
</body>

Вот обновленный плункер, показывающий новое поведение: http://plnkr.co/edit/Lxem5HJXe0UCvslqbJr3?p=preview

person Sly_cardinal    schedule 05.06.2014
comment
сегодня работает интерполяция - см. источник: github .com/angular/angular.js/blob/master/src/ng/directive/ — вы можете получить состояния $dirty $invalid для конкретной вложенной формы ngForm по имени. Множество возможностей. - person Leblanc Meneses; 17.01.2015
comment
@LeblancMeneses Спасибо за это, похоже, поддержка интерполяции была добавлена ​​​​в Angular 1.3. - person Sly_cardinal; 17.01.2015
comment
Спасибо. Однако поддержка интерполяции в ngForm и ‹input name=› имеет ограниченное значение для решения этой проблемы, если вы используете ngMessages. ng-messages не поддерживает интерполяцию, поэтому нет возможности сослаться на конкретную форму в элементе ngMessages — он просто выдает ошибку. - person greenie2600; 03.09.2015
comment
Динамически созданные формы недоступны в контроллере. Иногда только в том случае, если переменная области видимости объявлена ​​в контроллере, она доступна из представления контроллеру. Я получаю неопределенность для динамически созданных переменных при доступе - person Jeeva J; 16.09.2015
comment
@JeevaJsb вы должны использовать такое имя, как myforms.namesForm_{{$index}}, и инициализировать его в контроллере $scope.myforms = {}, чтобы ссылка не была нарушена, и вы могли использовать ее правильно. - person Maddis; 24.03.2016
comment
в какой именно версии Angular 1.3 стала поддерживаться интерполяция? - person xxlali; 31.03.2016
comment
@xxlali Динамическая интерполяция доступна во всех выпусках Angular 1.3.0+ (она была добавлена ​​в 1.3.0-rc.3, поэтому стала доступна, как только была полностью выпущена версия 1.3.0). - person Sly_cardinal; 31.03.2016