route - Con AngularJS, ¿cómo configuro todos los campos de formulario en $ sucio a la vez?
navigationend example (4)
Cuando un formulario se invalida, FormController contiene una propiedad $error
.
$error
Es un hash de objeto, que contiene referencias a controles o formularios con validadores fallidos.
Así que simplemente puede recorrer el objeto de error y usar $setDirty()
en cada control:
// "If the name attribute is specified, the form controller is published onto the current scope under this name."
var form = scope.myForm;
if (form.$invalid) {
angular.forEach(form.$error, function(controls, errorName) {
angular.forEach(controls, function(control) {
control.$setDirty();
});
});
}
He creado un formulario HTML utilizando AngularJS y he agregado required
atributos required
a algunos campos.
Para estos campos, tengo un mensaje de error que muestra si el campo no es $pristine
y también $invalid
:
<input type="text" ng-model="SomeModel.SomeProperty" name="myField" class="input-block-level" required>
<p ng-show="frmMyForm.myField.$invalid && !frmMyForm.myField.$pristine" class="error-text">This field is required!</p>
Esto funciona bien. Sin embargo, si el usuario simplemente omite un campo requerido (nunca coloca el cursor en él), entonces el campo siempre estará prístino y, por lo tanto, el mensaje de error no se mostrará, incluso después de hacer clic en el botón Enviar. Por lo tanto, el usuario se enfrenta a un formulario que no puede enviar pero no a un texto de error que le diga por qué.
Mi idea es que si se configuran todos los campos de formulario en $dirty
en la acción de envío, se activarán los mensajes de error que aparecerán en cualquier campo obligatorio que el usuario simplemente omita. es posible? ¿Si es así, cómo?
Gracias por adelantado.
Hacemos algo similar a su respuesta, tenemos una directiva de formulario enviado que se enlaza con el evento de envío, si se activa, establecemos la variable $ enviada en el controlador de formulario. De esa manera, puede usarlo de manera similar a como usa ShowValidationMessages, pero es reutilizable. Directiva muy sencilla:
app.directive(''formSubmitted'', [function () {
return {
restrict: ''A'',
require: ''form'',
link: function (scope, element, attrs, ctrl) {
ctrl.$submitted = false;
element.on(''submit'', function () {
scope.$apply(function () {
ctrl.$submitted = true;
});
});
}
};
}]);
Usted aplica esto en la etiqueta del formulario como un atributo.
Avanzamos un par de pasos más, nuestro requisito era mostrar los errores de validación solo si se cumple lo siguiente: el elemento no es válido Y el formulario fue enviado O el elemento de entrada está borroso. Así que terminamos con otra directiva que requiere ngModel que establece el estado borroso del elemento en el controlador ngModel.
Y finalmente deshacerse de una gran cantidad de códigos repetidos de calderas en html para verificar todo esto, por ejemplo, su ng-show="frmMyForm.myField.$invalid && (!frmMyForm.myField.$pristine || MyObject.ShowValidationMessages)"
encapsulamos eso en una directiva. Esta directiva de plantilla envuelve nuestros elementos de entrada con la placa de caldera de Bootstrap y maneja todo el material de validación. Así que ahora todas las entradas de mi formulario solo siguen este patrón:
<div data-bc-form-group data-label="Username:">
<input type="text" id="username" name="username" ng-model="vm.username" data-bc-focus required />
</div>
y la directiva bcFormGroup lo transforma en el siguiente html habilitado para bootstrap:
<div class="form-group" ng-class="{''has-error'': showFormGroupError()}" data-bc-form-group="" data-label="Username:">
<label for="username" class="col-md-3 control-label ng-binding">Username:</label>
<div class="col-md-9">
<input type="text" id="username" name="username" ng-model="vm.username" data-bc-focus="" required="" class="ng-pristine form-control ng-valid ng-valid-required">
<span class="help-block ng-hide" ng-show="showRequiredError()">Required</span>
</div>
</div>
Esto mantiene las cosas en SECO y ofrece una gran flexibilidad en cuanto a qué tipo de entradas son compatibles.
Actualizar:
Aquí hay una lista básica de la directiva bcFormGroup. La plantilla predeterminada utiliza la forma horizontal de bootstrap, pero puede adaptarse a su gusto.
app.directive(''bcFormGroup'', [''$compile'', ''$interpolate'', function ($compile, $interpolate) {
return {
restrict: ''A'',
template:
''<div class="form-group" ng-class="{/'has-error/': showFormGroupError()}">'' +
''<label for="{{inputId}}" class="col-md-3 control-label">{{label}}</label>'' +
''<div class="col-md-9">'' +
''<bc-placeholder></bc-placeholder>'' +
''</div>'' +
''</div>'',
replace: true,
transclude: true,
require: ''^form'',
scope: {
label: ''@'',
inputTag: ''@''
},
link: function (scope, element, attrs, formController, transcludeFn) {
transcludeFn(function (clone) {
var placeholder = element.find(''bc-placeholder'');
placeholder.replaceWith(clone);
});
var inputTagType = scope.inputTag || ''input'';
var inputElement = element.find(inputTagType);
var fqFieldName = formController.$name + ''.'' + inputElement.attr(''name'');
var formScope = inputElement.scope();
if (inputElement.attr(''type'') !== ''checkbox'' && inputElement.attr(''type'') !== ''file'') {
inputElement.addClass(''form-control'');
}
scope.inputId = $interpolate(inputElement.attr(''id''))(formScope);
scope.hasError = false;
scope.submitted = false;
formScope.$watch(fqFieldName + ''.$invalid'', function (hasError) {
scope.hasError = hasError;
});
formScope.$watch(formController.$name + ''.$submitted'', function (submitted) {
scope.submitted = submitted;
});
if (inputElement.attr(''data-bc-focus'') != null || inputElement.attr(''bc-focus'') != null) {
scope.hasBlurred = false;
formScope.$watch(fqFieldName + ''.$hasBlurred'', function (hasBlurred) {
scope.hasBlurred = hasBlurred;
});
}
if (inputElement.attr(''required'')) {
scope.hasRequiredError = false;
formScope.$watch(fqFieldName + ''.$error.required'', function (required) {
scope.hasRequiredError = required;
});
inputElement.after($compile(''<span class="help-block" ng-show="showRequiredError()">Required</span>'')(scope));
}
if (inputElement.attr(''type'') === ''email'') {
scope.hasEmailError = false;
formScope.$watch(fqFieldName + ''.$error.email'', function (emailError) {
scope.hasEmailError = emailError;
});
inputElement.after($compile(''<span class="help-block" ng-show="showEmailError()">Invalid email address</span>'')(scope));
}
scope.showFormGroupError = function () {
return scope.hasError && (scope.submitted || (scope.hasBlurred === true));
};
scope.showRequiredError = function () {
return scope.hasRequiredError && (scope.submitted || (scope.hasBlurred === true));
};
scope.showEmailError = function () {
return scope.hasEmailError && (scope.submitted || (scope.hasBlurred === true));
};
}
};
}]);
Actualizar:
La siguiente directiva establece $ focus y $ hasBlurred:
app.directive(''bcFocus'', [function () {
var focusClass = ''bc-focused'';
return {
restrict: ''A'',
require: ''ngModel'',
link: function (scope, element, attrs, ctrl) {
ctrl.$focused = false;
ctrl.$hasBlurred = false;
element.on(''focus'', function () {
element.addClass(focusClass);
var phase = scope.$root.$$phase;
if (phase == ''$apply'' || phase == ''$digest'') {
ctrl.$focused = true;
} else {
scope.$apply(function () {
ctrl.$focused = true;
});
}
}).on(''blur'', function () {
element.removeClass(focusClass);
var phase = scope.$root.$$phase;
if (phase == ''$apply'' || phase == ''$digest'') {
ctrl.$focused = false;
ctrl.$hasBlurred = true;
} else {
scope.$apply(function () {
ctrl.$focused = false;
ctrl.$hasBlurred = true;
});
}
});
}
};
}]);
Hice algo simple.
$scope.FormName.InputName.$pristine = false;
if($scope.FormName.$invalid)
return;
Se me ocurrió una respuesta poco después de publicar esto. No estoy seguro de si es la forma correcta , pero funciona.
En tu controlador, simplemente agrega una propiedad para "ShowValidationMessages" o algo similar y configúralo en false
:
$scope.MyObject = {
ShowValidationMessages: false
};
Ahora haga referencia a este valor en su lógica de validación de nivel de campo:
<p ng-show="frmMyForm.myField.$invalid && (!frmMyForm.myField.$pristine || MyObject.ShowValidationMessages)" class="error-text">This field is required!</p>
Finalmente, cambie la propiedad ShowValidationMessages
a true
en su función de envío de formulario:
$scope.MyObject = {
ShowValidationMessages: false,
SubmitForm: function(){
$scope.MyObject.ShowValidationMessages = true;
if($scope.frmMyForm.$valid){
//do stuff
}
}
};