Validador angular2 que se basa en múltiples campos de formulario

¿Es posible crear un validador que pueda usar múltiples valores para decidir si mi campo es válido?

por ejemplo, si el método de contacto preferido del cliente es por correo electrónico, entonces el campo de correo electrónico debe ser requerido.

Gracias.


Actualizado con código de ejemplo …


import {Component, View} from 'angular2/angular2'; import {FormBuilder, Validators, formDirectives, ControlGroup} from 'angular2/forms'; @Component({ selector: 'customer-basic', viewInjector: [FormBuilder] }) @View({ templateUrl: 'app/components/customerBasic/customerBasic.html', directives: [formDirectives] }) export class CustomerBasic { customerForm: ControlGroup; constructor(builder: FormBuilder) { this.customerForm = builder.group({ firstname: [''], lastname: [''], validateZip: ['yes'], zipcode: ['', this.zipCodeValidator] // I only want to validate using the function below if the validateZip control is set to 'yes' }); } zipCodeValidator(control) { if (!control.value.match(/\d\d\d\d\d(-\d\d\d\d)?/)) { return { invalidZipCode: true }; } } } 

Para reiterar los métodos que otros han publicado, esta es la forma en que he estado creando validadores de FormGroup que no involucran múltiples grupos.

Para este ejemplo, simplemente proporcione los nombres clave de la password y confirmPassword campos confirmPassword .

 // Example use of FormBuilder, FormGroups, and FormControls this.registrationForm = fb.group({ dob: ['', Validators.required], email: ['', Validators.compose([Validators.required, emailValidator])], password: ['', Validators.required], confirmPassword: ['', Validators.required], firstName: ['', Validators.required], lastName: ['', Validators.required] }, {validator: matchingPasswords('password', 'confirmPassword')}) 

Para que los Validators tomen parámetros, necesitan devolver una function con un FormGroup o un FormGroup FormControl como parámetro. En este caso, estoy validando un FormGroup .

 function matchingPasswords(passwordKey: string, confirmPasswordKey: string) { return (group: FormGroup): {[key: string]: any} => { let password = group.controls[passwordKey]; let confirmPassword = group.controls[confirmPasswordKey]; if (password.value !== confirmPassword.value) { return { mismatchedPasswords: true }; } } } 

Técnicamente, podría haber validado dos valores si conocía sus claves, pero prefiero nombrar mis Validators la misma forma que el error que devolverán. La función podría modificarse para tomar un tercer parámetro que represente el nombre de la clave del error devuelto.

Actualizado 6 de diciembre de 2016 (v2.2.4)

Ejemplo completo: https://embed.plnkr.co/ukwCXm/

La respuesta de Dave fue muy, muy útil. Sin embargo, una pequeña modificación podría ayudar a algunas personas.

En caso de que necesite agregar errores a los campos de Control , puede mantener la construcción real del formulario y los validadores:

 // Example use of FormBuilder, ControlGroups, and Controls this.registrationForm= fb.group({ dob: ['', Validators.required], email: ['', Validators.compose([Validators.required, emailValidator])], password: ['', Validators.required], confirmPassword: ['', Validators.required], firstName: ['', Validators.required], lastName: ['', Validators.required] }, {validator: matchingPasswords('password', 'confirmPassword')}) 

En lugar de establecer un error en el ControlGroup , hágalo en el campo real de la siguiente manera:

 function matchingPasswords(passwordKey: string, passwordConfirmationKey: string) { return (group: ControlGroup) => { let passwordInput = group.controls[passwordKey]; let passwordConfirmationInput = group.controls[passwordConfirmationKey]; if (passwordInput.value !== passwordConfirmationInput.value) { return passwordConfirmationInput.setErrors({notEquivalent: true}) } } } 

Estoy usando Angular 2 RC.5 pero no pude encontrar el ControlGroup, basado en la respuesta útil de Dave. Descubrí que FormGroup funciona en su lugar. Así que hice algunas actualizaciones menores sobre los códigos de Dave, y pensé que compartiría con otros.

En su archivo de componente, agregue una importación para FormGroup:

 import {FormGroup} from "@angular/forms"; 

Defina sus entradas en caso de que necesite acceder al control de formulario directamente:

 oldPassword = new FormControl("", Validators.required); newPassword = new FormControl("", Validators.required); newPasswordAgain = new FormControl("", Validators.required); 

En tu constructor, ejemplifica tu formulario:

 this.form = fb.group({ "oldPassword": this.oldPassword, "newPassword": this.newPassword, "newPasswordAgain": this.newPasswordAgain }, {validator: this.matchingPasswords('newPassword', 'newPasswordAgain')}); 

Agregue la función matchingPasswords en su clase:

 matchingPasswords(passwordKey: string, passwordConfirmationKey: string) { return (group: FormGroup) => { let passwordInput = group.controls[passwordKey]; let passwordConfirmationInput = group.controls[passwordConfirmationKey]; if (passwordInput.value !== passwordConfirmationInput.value) { return passwordConfirmationInput.setErrors({notEquivalent: true}) } } } 

Espero que esto ayude a aquellos que están usando RC.5. Tenga en cuenta que aún no he probado en RC.6.

Al implementar validadores para múltiples campos de formulario, deberá asegurarse de que los validadores se vuelvan a evaluar cuando se actualiza cada uno de los controles de formulario. La mayoría de los ejemplos no proporcionan una solución para dicho escenario, pero esto es muy importante para la coherencia de los datos y el comportamiento correcto.

Consulte mi implementación de un validador personalizado para Angular 2, que tiene esto en cuenta: https://gist.github.com/slavafomin/17ded0e723a7d3216fb3d8bf845c2f30 .

Estoy usando otherControl.valueChanges.subscribe() para escuchar los cambios en otro control y thisControl.updateValueAndValidity() para activar otra ronda de validación cuando se cambia otro control.


Estoy copiando un código a continuación para referencia futura:

match-other-validator.ts

 import {FormControl} from '@angular/forms'; export function matchOtherValidator (otherControlName: string) { let thisControl: FormControl; let otherControl: FormControl; return function matchOtherValidate (control: FormControl) { if (!control.parent) { return null; } // Initializing the validator. if (!thisControl) { thisControl = control; otherControl = control.parent.get(otherControlName) as FormControl; if (!otherControl) { throw new Error('matchOtherValidator(): other control is not found in parent group'); } otherControl.valueChanges.subscribe(() => { thisControl.updateValueAndValidity(); }); } if (!otherControl) { return null; } if (otherControl.value !== thisControl.value) { return { matchOther: true }; } return null; } } 

Uso

Así es como puedes usarlo con formas reactivas:

 private constructForm () { this.form = this.formBuilder.group({ email: ['', [ Validators.required, Validators.email ]], password: ['', Validators.required], repeatPassword: ['', [ Validators.required, matchOtherValidator('password') ]] }); } 

Se pueden encontrar validadores más actualizados aquí: moebius-mlm / ng-validators .

Para ampliar la respuesta de matthewdaniel ya que no es exactamente correcto. Aquí hay un código de ejemplo que muestra cómo asignar correctamente un validador a un ControlGroup .

 import {Component} from angular2/core import {FormBuilder, Control, ControlGroup, Validators} from 'angular2/common' @Component({ selector: 'my-app', template: ` 



Valid?: {{form.valid}}

{{form.value | json}}

` }) export class App { form: ControlGroup constructor(fb: FormBuilder) { this.form = fb.group({ name: ['', Validators.required], email: ['', Validators.required] matchingPassword: fb.group({ password: ['', Validators.required], confirmPassword: ['', Validators.required] }, {validator: this.areEqual}) }); } areEqual(group: ControlGroup) { let val; let valid = true; for (name in group.controls) { if (val === undefined) { val = group.controls[name].value } else { if (val !== group.controls[name].value) { valid = false; break; } } } if (valid) { return null; } return { areEqual: true }; } }

Aquí hay un ejemplo de trabajo: http://plnkr.co/edit/Zcbg2T3tOxYmhxs7vaAm?p=preview

Montones de excavaciones en fuente angular, pero encontré una mejor manera.

 constructor(...) { this.formGroup = builder.group({ first_name: ['', Validators.required], matching_password: builder.group({ password: ['', Validators.required], confirm: ['', Validators.required] }, this.matchPassword) }); // expose easy access to passworGroup to html this.passwordGroup = this.formGroup.controls.matching_password; } matchPassword(group): any { let password = group.controls.password; let confirm = group.controls.confirm; // Don't kick in until user touches both fields if (password.pristine || confirm.pristine) { return null; } // Mark group as touched so we can add invalid class easily group.markAsTouched(); if (password.value === confirm.value) { return null; } return { isValid: false }; } 

Porción de HTML para grupo de contraseñas

 
Passwords must match.

Aquí hay otra opción que pude encontrar que no depende de un ControlGroup completo o secundario, sino que está vinculada directamente a cada Control .

El problema que tuve fue que los controles que dependían uno del otro no estaban jerárquicamente juntos, así que no ControlGroup crear un ControlGroup . Además, mi CSS se configuró para que cada control aprovechara las clases angulares existentes para determinar si mostrar el estilo de error, que era más complicado cuando se trata de una validación de grupo en lugar de una validación específica de control. Tratar de determinar si un único control era válido no era posible ya que la validación estaba ligada al grupo de controles y no a cada control individual.

En mi caso, quería el valor de un cuadro de selección para determinar si se necesitaría otro campo o no.

Esto se crea utilizando el Creador de Formularios en el componente. Para el modelo de selección en lugar de vincularlo directamente al valor del objeto de solicitud, lo he vinculado para obtener / establecer funciones que me permitan manejar eventos “en cambio” para el control. Entonces podré configurar manualmente la validación para otro control dependiendo de la selección de controles de nuevo valor.

Aquí está la porción de vista relevante:

  ...  

La parte del componente relevante:

 export class RequestComponent { form: ControlGroup; request: RequestItem; constructor(private fb: FormBuilder) { this.form = fb.group({ employee: new Control("", Validators.required), empID: new Control("", Validators.compose([Validators.pattern("[0-9]{7}"])) }); get employeeModel() { return this.request.isEmployee; } set employeeModel(value) { this.request.isEmployee = value; if (value === "Yes") { this.form.controls["empID"].validator = Validators.compose([Validators.pattern("[0-9]{7}"), Validators.required]); this.form.controls["empID"].updateValueAndValidity(); } else { this.form.controls["empID"].validator = Validators.compose([Validators.pattern("[0-9]{7}")]); this.form.controls["empID"].updateValueAndValidity(); } } } 

En mi caso, siempre tuve una validación de patrón vinculada al control, por lo que el validator siempre está configurado en algo, pero creo que puede establecer el validator como nulo si no tiene ninguna validación vinculada al control.

ACTUALIZACIÓN: hay otros métodos para capturar el cambio de modelo como (ngModelChange)=changeFunctionName($event) o suscribirse para controlar los cambios de valor utilizando this.form.controls["employee"].valueChanges.subscribe(data => ...))

Intenté la mayoría de estas respuestas, pero ninguna de ellas funcionó para mí. Encontré un ejemplo que funciona aquí https://scotch.io/@ibrahimalsurkhi/match-password-validation-with-angular-2

Estaba buscando esto también y terminé usando equalTo desde el paquete ng2-validation ( https://www.npmjs.com/package/ng2-validation )

Aquí hay un ejemplo: Conducido por una plantilla:

  

required error

equalTo error

Model Driven:

 let password = new FormControl('', Validators.required); let certainPassword = new FormControl('', CustomValidators.equalTo(password)); this.form = new FormGroup({ password: password, certainPassword: certainPassword }); 

Modelo:

 

required error

equalTo error

Aquí está mi versión que utilicé para asegurar que la edad en un campo sea mayor o igual que la edad en otro campo. También estoy usando grupos de formularios, así que utilizo la función group.get lugar de group.controls[]

 import { FormGroup } from '@angular/forms'; export function greaterThanOrEqualTo(sourceKey: string, targetKey: string) { return (group: FormGroup) => { let sourceInput = group.get(sourceKey); let targetInput = group.get(targetKey); console.log(sourceInput); console.log(targetInput); if (targetInput.value < sourceInput.value) { return targetInput.setErrors({ notGreaterThanOrEqualTo: true }) } } } 

Y en el componente:

  this.form = this._fb.group({ clientDetails: this._fb.group({ currentAge: ['', [Validators.required, Validators.pattern('^((1[89])|([2-9][0-9])|100)$')]], expectedRetirementAge: ['', [Validators.required]] }), }, { validator: greaterThanOrEqualTo('clientDetails.currentAge', 'clientDetails.expectedRetirementAge') }); 

Creo que tu mejor opción, por ahora, es crear un grupo de formularios para mantener tus controles. Cuando crea una instancia de su Pase de control en la función para validarlo. ejemplo:

  this.password = new Control('', Validators.required); let x = this.password; this.confirm = new Control('', function(c: Control){ if(typeof c.value === 'undefined' || c.value == "") return {required: "password required"}; if(c.value !== x.value) return {error: "password mismatch"}; return null; }); 

Sé que esto es altamente dependiente de la versión de angularjs2 que está ejecutando. Esto fue probado contra 2.0.0-alpha.46

Si alguien tiene una mejor sugestión, como escribir un validador personalizado (que puede ser la mejor manera de hacerlo), es bienvenido.

EDITAR

también puedes usar ControlGroup y validar ese grupo por completo.

 this.formGroup = new ControlGroup({}, function(c: ControlGroup){ var pass: Control = c.controls["password"]; var conf: Control = c.controls["confirm"]; pass.setErrors(null, true); if(pass.value != null && pass.value != ""){ if(conf.value != pass.value){ pass.setErrors({error: "invalid"}, true); return {error: "error"}; } } return null; }); 

Simplemente edite los mensajes de acuerdo a su dominio.

La respuesta de Louis Cruz fue muy útil para mí.

Para completar, simplemente agregue en el rest el restablecimiento de setErrors: return passwordConfirmationInput.setErrors (null);

¡Y todo funciona bien!

Gracias,

Saludos,

TGA

Sugeriría usar la biblioteca ng-form-rules . Es una biblioteca increíble para crear todos los tipos de formularios con la lógica de validación desacoplada del componente y que puede depender de los cambios de valores de otras áreas del formulario. Tienen una gran documentación , ejemplos y un video que muestra muchas de sus funciones . Hacer la validación así, lo que estás tratando de hacer es trivial.

Puede consultar su LÉAME para obtener información de alto nivel y un ejemplo básico.

Reglas de validación de coincidencia de contraseñas Angular 4.

Si necesita controlar los campos de los errores, puede hacerlo.

 createForm() { this.ngForm = this.fb.group({ 'first_name': ["", Validators.required ], 'last_name' : ["", Validators.compose([Validators.required, Validators.minLength(3)]) ], 'status' : ['active', Validators.compose([Validators.required])], 'phone':[null], 'gender':['male'], 'address':[''], 'email':['', Validators.compose([ Validators.required, Validators.email])], 'password':['', Validators.compose([Validators.required])], 'confirm_password':['', Validators.compose([Validators.required])] }, {validator: this.matchingPassword('password', 'confirm_password')}); } 

Entonces su necesidad de declarar este método en el método constructor Like as.

 constructor( private fb: FormBuilder ) { this.createForm(); } 

En lugar de establecer un error en el ControlGroup, hágalo en el campo real de la siguiente manera:

  matchingPassword(passwordKey: string, confirmPasswordKey: string) { return (group: FormGroup): {[key: string]: any} => { let password = group.controls[passwordKey]; let confirm_password = group.controls[confirmPasswordKey]; if (password.value !== confirm_password.value) { return { mismatchedPasswords: true }; } } } 

Porción de HTML para grupo de contraseñas

 
This Field is Required.
{{ngForm.value.password | json}}
Passwords doesn't match.