Подтвердить что ты не робот

Пропустить проверку вложенных форм с помощью AngularJS

Как я могу пропустить проверку вложенных форм с помощью AngularJS? Я должен сделать внешнюю форму действительной, даже если ее дочерняя форма недействительна.

В приведенном ниже примере внешняя форма должна быть действительной (fOuter.$valid должно быть истинно). По умолчанию это не так. Есть ли опция?

Код (jsFiddle):

<div ng-app ng-controller="Ctrl">  
    <ng-form name="fOuter">  
        <h3>Outer form (valid={{fOuter.$valid}})</h3>  
        <input type="text" name="txtOuter" ng-model="outer" placeholder="(required)" required />  
        <ng-form name="fInner">  
            <h3>Inner form (valid={{fInner.$valid}})</h3>  
            <input type="text" name="txtInner" ng-model="inner" placeholder="(required)" required />  
        </ng-form>  
    </ng-form>  
</div>
4b9b3361

Ответ 1

В Angular формы могут быть вложенными. Это означает, что внешняя форма действительна, когда все дочерние формы также действительны.

Таким образом, нет возможности сделать внешнюю форму действительной автоматически (через клавишу $valid), если один из внутренних недействителен.

Попробуйте использовать error.required

   <h3>Outer form (valid={{!fOuter.txtOuter.$error.required}})</h3>

Демо Fiddle

Из Angular ngForm docs:

Другим способом должно быть использование контроллера, например:

<h3>Outer form (valid={{isOuterFormValid}})</h3>

контроллер

$scope.isOuterFormValid = true;

// here, add listener on each input and change flag `isOuterFormValid`
... 

Ответ 2

Вот мое решение, вдохновленное мбернатом, которое полностью изолирует форму от своего отца.

Это решение позаботится о:

  • Срок действия формы ($ valid, $недействителен)
  • Взаимодействие с форматом ($ чистая, $грязная)
  • Действительность и взаимодействие вложенных форм

Смотрите в действии в JSFiddle.

angular.module('isolateForm',[]).directive('isolateForm', [function () {
    return {
        restrict: 'A',
        require: '?form',
        link: function (scope, elm, attrs, ctrl) {
            if (!ctrl) {
                return;
            }

            // Do a copy of the controller
            var ctrlCopy = {};
            angular.copy(ctrl, ctrlCopy);

            // Get the parent of the form
            var parent = elm.parent().controller('form');
            // Remove parent link to the controller
            parent.$removeControl(ctrl);

            // Replace form controller with a "isolated form"
            var isolatedFormCtrl = {
                $setValidity: function (validationToken, isValid, control) {
                    ctrlCopy.$setValidity(validationToken, isValid, control);
                    parent.$setValidity(validationToken, true, ctrl);
                },
                $setDirty: function () {
                    elm.removeClass('ng-pristine').addClass('ng-dirty');
                    ctrl.$dirty = true;
                    ctrl.$pristine = false;
                },
            };
            angular.extend(ctrl, isolatedFormCtrl);
        }
    };
}]);

Чтобы использовать его, просто вызовите директиву "isolate-form":

<form name="parent">
    <input type="text" ng-model="outside"/>
    <ng-form name="subform" isolate-form>
        <input type="text" ng-model="inside"/>
    </ng-form>
</form>

Ответ 3

Я столкнулся с той же проблемой. В большей форме мне нужно было иметь подчиненную форму с несколькими элементами управления, которые не должны касаться состояния родительской формы.

Здесь мое решение: я написал директиву "null-form", которая удаляет подчиненную форму из родительской формы и которая не отправляет какие-либо изменения состояния родителя.

angular.module('nullForm',[]).directive('nullForm', [function () {
  return {
    restrict: 'A',
    require: '?form',
    link: function link(scope, element, iAttrs, formController) {

      if (! formController) {
        return;
      }

      // Remove this form from parent controller
      var parentFormController = element.parent().controller('form');
      parentFormController.$removeControl(formController);

      // Replace form controller with a "null-controller"
      var nullFormCtrl = {
        $addControl: angular.noop,
        $removeControl: angular.noop,
        $setValidity: angular.noop,
        $setDirty: angular.noop,
        $setPristine: angular.noop
      };

      angular.extend(formController, nullFormCtrl);
    }
  };
}]);

Затем вы можете использовать его следующим образом:

<form name="parent">
  <input type="text" ng-model="outside"/>
  <ng-form name="subform" null-form>
    <input type="text" ng-model="inside"/>
  </ng-form>
</form>

Любое изменение или отрицательная проверка "внутри" не будет влиять на "родительский".

Тем не менее, один недостаток из-за этого решения: subform не будет иметь никакого состояния, и не будут работать его классы CSS, такие как ng-invalid и т.д. Для этого вам нужно будет повторно реализовать эту функцию с помощью исходного контроллера формы.

Ответ 4

По крайней мере, с Angular 1.5, кажется, достаточно удалить вложенную форму из родителя с помощью $removeControl:

module.directive('isolateForm', function() {
  return {
    restrict: 'A',
    require: '?form',
    link: function(scope, element, attrs, formController) {
      if (!formController) {
        return;
      }

      var parentForm = formController.$$parentForm; // Note this uses private API
      if (!parentForm) {
        return;
      }

      // Remove this form from parent controller
      parentForm.$removeControl(formController);
    }
  };
});

Et voila, нетронутые и действительные состояния родителя больше не затрагиваются вложенной формой.

Ответ 5

Я нашел решение, которое лучше всего работало, было у Антона.

Установка nullFormCtrl, предложенная mbernath, отключает проверку на дочерней форме (спасибоs для прокладывания пути, хотя...).

Единственное изменение, которое я сделал, это способ доступа к parentForm. angular предоставляет метод для этого.

.directive('isolateForm', [function () {
    return {
        restrict: 'A',
        require: '?form',
        link: function link(scope, element, iAttrs, formController) {

            if (!formController) {
                return;
            }

            // Remove this form from parent controller
            formController.$$parentForm.$removeControl(formController)

            var _handler = formController.$setValidity;
            formController.$setValidity = function (validationErrorKey, isValid, cntrl) {
                _handler(validationErrorKey, isValid, cntrl);
                formController.$$parentForm.$setValidity(validationErrorKey, true, this);
            }
        }
    };
}]);

Ответ 6

Я новичок в Angular, однако, пожалуйста, проверьте, помогает ли подход ниже.

<div ng-app ng-controller="Ctrl"> <ng-form name="fOuter"> <h3>Outer form (valid={{fOuter.$valid}})</h3> <ng-form name="fInner1"> <h3>Inner form 1 (valid={{fInner1.$valid}})</h3> <input type="text" name="txtInner1" ng-model="outer" placeholder="(required)" required /> </ng-form> <ng-form name="fInner2"> <h3>Inner form 2 (valid={{fInner2.$valid}})</h3> <input type="text" name="txtInner2" ng-model="inner" placeholder="(required)" required /> </ng-form> </ng-form> </div>

Ответ 7

У меня была такая же проблема и разрешила ее с изменением бита в локальной копии самого файла angular.js.

В принципе, я добавил новую функцию в FormController, как показано ниже:

form.$resetParent = function() {
    parentForm = nullFormCtrl;
};

и создайте настраиваемую директиву:

angular.module('myApp').directive('dtIsolatedForm', function () {
    return {
        restrict: 'A',
        require: '?form',
        link: function (scope, element, attrs, formController) {
            if (!formController || !formController.$parentForm) {
                return;
            }

            formController.$resetParent();
        }
    };
});

Ответ 8

Также, вдохновленный mbernath, я нашел более простое решение. Он состоит из создания фиктивной формы-подобной директивы только для изоляции. Директива останавливает распространение из вложенных элементов во внешнюю форму, но не имеет никакой функциональности формы. Вы можете встраивать ngForm внутри и полностью функционировать.

angular.directive('formIsolator', function () {

            return {
                name: 'form',
                restrict: 'EAC',
                controller: function() {
                    this.$addControl = angular.noop;
                    this.$$renameControl = function(control, name) {
                        control.$name = name;
                    };
                    this.$removeControl = angular.noop;
                    this.$setValidity = angular.noop;
                    this.$setDirty = angular.noop;
                    this.$setPristine = angular.noop;
                    this.$setSubmitted = angular.noop;
                }
            };
        })

Способ указывать имя контроллера в определении директивы (name: 'form'). Это свойство не документировано, но используется для создания директивы ngForm в angular source.

Ответ 9

Я хотел бы предложить версию mbernath без проблем

angular.module('yourModule').directive('isolatedForm', [function () {
return {
    restrict: 'A',
    require: '?form',
    link: function link(scope, element, iAttrs, formController) {

        if (!formController) return;

        // Remove this form from parent controller
        var parentFormController = element.parent().controller('form');
        parentFormController.$removeControl(formController);
        // override default behavior
        var _handler = formController.$setValidity;
        formController.$setValidity = function (validationErrorKey, isValid, cntrl) {
            _handler(validationErrorKey, isValid, cntrl);
            parentFormController.$setValidity(validationErrorKey, true, this);
        }
    }
};}]);

Ответ 10

С вашего контроллера:

Ctrl.isOuterFormValid = function() {
    var outerFormIsValid = true;
    for(var prop in Ctrl.formName) {
        //The form is only inValid if the property is not a new form and it is invalid
        if(pvCtrl.pvForm[prop].constructor.name !== "FormController" &&
           pvCtrl.pvForm[prop].$invalid){
            outerFormIsValid = false;
        }
    }

    alert(outerFormIsValid);
};

FormController - это объект, который предоставляет вам информацию о состоянии вашей формы.
Добавление формы в форму с помощью ng-form добавляет свойство FormController к исходному объекту FormController.

Это имеет то преимущество, что вы не добавляете директиву html ко всем вашим входным элементам.

Ответ 11

В принципе, цель состоит в том, чтобы разделить соединение между вложенными формами и выполнить собственную проверку/доступ к $error объектам форм независимо. Это можно сделать, введя modelController между двумя вложенными формами и позволяя modelController определять, когда контроллер родительской формы и контроллер дочерней формы должны быть действительными/недействительными. Это может быть достигнуто путем увеличения значения $setValidity(), которое определяет, когда форма должна быть действительной/недействительной.

Пожалуйста, найдите мой код в ссылке plunker ниже. Я ввел модель контроллера между родительской и дочерней формой. Здесь я отвлек $error объект дочерней формы из родительской формы. Смысл, родительская форма не сможет увидеть, что не так с дочерней формой, но она будет признана недействительной, если какое-то поле недействительно в дочерней форме. Только промежуточная modelController знает, какие поля имеют проблемы в дочерней форме. Эта логика может быть изменена или расширенный на основе наших потребностей. Пожалуйста, дайте мне знать, если кому-то нужно уточнение с точки зрения кода.

[plnkr]: https://plnkr.co/edit/5gvctSSqmWiEAUE3YUcZ?p=preview