validaciones - Validación dinámica y nombre en un formulario con AngularJS
validar formulario angular 6 (8)
Amplío un poco la solución @caitp y @Thinkscape para permitir ng-forms anidadas creadas dinámicamente, como esta:
<div ng-controller="ctrl">
<ng-form name="form">
<input type="text" ng-model="static" name="static"/>
<div ng-repeat="df in dynamicForms">
<ng-form name="form{{df.id}}">
<input type="text" ng-model="df.sub" name="sub"/>
<div>Dirty: <span ng-bind="form{{df.id}}.$dirty"></span></div>
</ng-form>
</div>
<div><button ng-click="consoleLog()">Console Log</button></div>
<div>Dirty: <span ng-bind="form.$dirty"></span></div>
</ng-form>
</div>
Aquí está mi demo en JSFiddle .
Tengo este formulario: http://jsfiddle.net/dfJeN/
Como puede ver, el valor del nombre para la entrada está establecido estáticamente:
name="username"
, la validación del formulario funciona bien (agregar algo y eliminar todo el texto de la entrada, debe aparecer un texto).
Luego trato de establecer dinámicamente el valor del nombre: http://jsfiddle.net/jNWB8/
name="{input.name}"
Luego aplico esto a mi validación
login.{{input.name}}.$error.required
(este patrón se usará en una repetición ng) pero la validación de mi formulario está rota. Se interpreta correctamente en mi navegador (si inspecciono el elemento vi login.username. $ Error.required).
Alguna idea ?
EDITAR: después de registrar el alcance en la consola, parece que el
{{input.name}}
la expresión no es interpolar Mi formulario como un atributo {{input.name}} pero sin nombre de usuario.
ACTUALIZACIÓN: desde 1.3.0-rc.3 name = "{{input.name}}" funciona como se esperaba. Por favor mira #1404
Bonito por @EnISeeK ... pero lo hice para ser más elegante y menos molesto para otras directivas:
.directive("dynamicName",[function(){
return {
restrict:"A",
require: [''ngModel'', ''^form''],
link:function(scope,element,attrs,ctrls){
ctrls[0].$name = scope.$eval(attrs.dynamicName) || attrs.dynamicName;
ctrls[1].$addControl(ctrls[0]);
}
};
}])
El problema debería solucionarse en AngularJS 1.3, según esta discusión en #1404 .
Mientras tanto, aquí hay una solución temporal creada por @caitp y @Thinkscape :
// Workaround for bug #1404
// https://github.com/angular/angular.js/issues/1404
// Source: http://plnkr.co/edit/hSMzWC?p=preview
app.config([''$provide'', function($provide) {
$provide.decorator(''ngModelDirective'', function($delegate) {
var ngModel = $delegate[0], controller = ngModel.controller;
ngModel.controller = [''$scope'', ''$element'', ''$attrs'', ''$injector'', function(scope, element, attrs, $injector) {
var $interpolate = $injector.get(''$interpolate'');
attrs.$set(''name'', $interpolate(attrs.name || '''')(scope));
$injector.invoke(controller, this, {
''$scope'': scope,
''$element'': element,
''$attrs'': attrs
});
}];
return $delegate;
});
$provide.decorator(''formDirective'', function($delegate) {
var form = $delegate[0], controller = form.controller;
form.controller = [''$scope'', ''$element'', ''$attrs'', ''$injector'', function(scope, element, attrs, $injector) {
var $interpolate = $injector.get(''$interpolate'');
attrs.$set(''name'', $interpolate(attrs.name || attrs.ngForm || '''')(scope));
$injector.invoke(controller, this, {
''$scope'': scope,
''$element'': element,
''$attrs'': attrs
});
}];
return $delegate;
});
}]);
Demostración en JSFiddle .
Este problema se ha solucionado en Angular 1.3+ Esta es la sintaxis correcta para lo que intenta hacer:
login[input.name].$invalid
No puedes hacer lo que intentas hacer de esa manera.
Asumiendo que lo que estás tratando de hacer es agregar dinámicamente elementos a un formulario, con algo así como una repetición ng, necesitas usar ng-form anidado para permitir la validación de esos ítems individuales:
<form name="outerForm">
<div ng-repeat="item in items">
<ng-form name="innerForm">
<input type="text" name="foo" ng-model="item.foo" />
<span ng-show="innerForm.foo.$error.required">required</span>
</ng-form>
</div>
<input type="submit" ng-disabled="outerForm.$invalid" />
</form>
Lamentablemente, simplemente no es una característica bien documentada de Angular.
Solo un poco de mejora sobre la solución EnlSeek
angular.module(''test'').directive(''dynamicName'', ["$parse", function($parse) {
return {
restrict: ''A'',
priority: 10000,
controller : ["$scope", "$element", "$attrs",
function($scope, $element, $attrs){
var name = $parse($attrs.dynamicName)($scope);
delete($attrs[''dynamicName'']);
$element.removeAttr(''data-dynamic-name'');
$element.removeAttr(''dynamic-name'');
$attrs.$set("name", name);
}]
};
}]);
Aquí hay una prueba de Plunker . Aquí hay una explicación detallada
Usar ngForm anidado le permite acceder al InputController específico desde la plantilla HTML. Sin embargo, si desea acceder desde otro controlador, no ayuda.
p.ej
<script>
function OuterController($scope) {
$scope.inputName = ''dynamicName'';
$scope.doStuff = function() {
console.log($scope.formName.dynamicName); // undefined
console.log($scope.formName.staticName); // InputController
}
}
</script>
<div controller=''OuterController''>
<form name=''myForm''>
<input name=''{{ inputName }}'' />
<input name=''staticName'' />
</form>
<a ng-click=''doStuff()''>Click</a>
</div>
Uso esta directiva para ayudar a resolver el problema:
angular.module(''test'').directive(''dynamicName'', function($compile, $parse) {
return {
restrict: ''A'',
terminal: true,
priority: 100000,
link: function(scope, elem) {
var name = $parse(elem.attr(''dynamic-name''))(scope);
// $interpolate() will support things like ''skill''+skill.id where parse will not
elem.removeAttr(''dynamic-name'');
elem.attr(''name'', name);
$compile(elem)(scope);
}
};
});
Ahora usa nombres dinámicos donde sea necesario solo el atributo ''nombre dinámico'' en lugar del atributo ''nombre''.
p.ej
<script>
function OuterController($scope) {
$scope.inputName = ''dynamicName'';
$scope.doStuff = function() {
console.log($scope.formName.dynamicName); // InputController
console.log($scope.formName.staticName); // InputController
}
}
</script>
<div controller=''OuterController''>
<form name=''myForm''>
<input dynamic-name=''inputName'' />
<input name=''staticName'' />
</form>
<a ng-click=''doStuff()''>Click</a>
</div>
Utilicé la solución de Ben Lesh y me funciona bien. Pero un problema al que me enfrenté fue que cuando agregué un formulario interno usando ng-form
, todos los estados del formulario, por ejemplo, form.$valid, form.$error
etc. quedaron sin definir si estaba usando la directiva ng-submit
.
Entonces, si tuviera esto, por ejemplo:
<form novalidate ng-submit="saveRecord()" name="outerForm">
<!--parts of the outer form-->
<ng-form name="inner-form">
<input name="someInput">
</ng-form>
<button type="submit">Submit</button>
</form>
Y en mi controlador:
$scope.saveRecord = function() {
outerForm.$valid // this is undefined
}
Así que tuve que volver a utilizar un evento de clic normal para enviar el formulario, en cuyo caso es necesario pasar el objeto de formulario:
<form novalidate name="outerForm"> <!--remove the ng-submit directive-->
<!--parts of the outer form-->
<ng-form name="inner-form">
<input name="someInput">
</ng-form>
<button type="submit" ng-click="saveRecord(outerForm)">Submit</button>
</form>
Y el método del controlador revisado:
$scope.saveRecord = function(outerForm) {
outerForm.$valid // this works
}
No estoy muy seguro de por qué es esto, pero espero que ayude a alguien.