Пропустить проверку вложенных форм с помощью 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>
Ответы
Ответ 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