O validador Angular2, que depende de vários campos de formulário

É possível criar um validador que possa usar vários valores para decidir se meu campo é válido?

Por exemplo, se o método de contato preferido do cliente for por e-mail, o campo de e-mail deverá ser solicitado.

Obrigado.


Atualizado com código de exemplo …


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 os methods que outros postaram, é assim que venho criando validadores do FormGroup que não envolvem vários grupos.

Para este exemplo, simplesmente forneça os nomes das chaves dos campos password e 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 os Validators tomem parâmetros, eles precisam retornar uma function com um FormGroup ou FormGroup como um parâmetro. Neste caso, estou validando um 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 }; } } } 

Tecnicamente, eu poderia ter validado quaisquer dois valores se eu soubesse suas chaves, mas prefiro nomear meus validadores da mesma forma que o erro que eles retornarão. A function pode ser modificada para obter um terceiro parâmetro que represente o nome da chave do erro retornado.

Atualizado 6 de dezembro de 2016 (v2.2.4)

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

A resposta de Dave foi muito, muito útil. No entanto, uma ligeira modificação pode ajudar algumas pessoas.

Caso você precise adicionar erros aos campos Control , você pode manter a construção real do formulário e dos 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')}) 

Em vez de definir um erro no ControlGroup , faça isso no campo real da seguinte forma:

 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}) } } } 

Estou usando o Angular 2 RC.5, mas não consegui encontrar o ControlGroup, com base na resposta útil do Dave. Eu descobri que o FormGroup funciona em vez disso. Então eu fiz algumas pequenas atualizações nos códigos de Dave, e pensei em compartilhar com os outros.

No seu arquivo de componente, adicione uma importação para o FormGroup:

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

Defina suas inputs caso você precise acessar o controle de formulário diretamente:

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

Em seu construtor, instancie seu formulário:

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

Adicione a function matchingPasswords na sua turma:

 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 isso ajude aqueles que estão usando o RC.5. Note que eu não testei no RC.6 ainda.

Ao implementar validadores para vários campos de formulário, você precisará garantir que os validadores sejam reavaliados quando cada um dos controles de formulário for atualizado. A maioria dos exemplos não fornece uma solução para esse cenário, mas isso é muito importante para a consistência dos dados e o comportamento correto.

Por favor, veja a minha implementação de um validador personalizado para o Angular 2, que leva isso em conta: https://gist.github.com/slavafomin/17ded0e723a7d3216fb3d8bf845c2f30 .

Eu estou usando otherControl.valueChanges.subscribe() para ouvir as alterações em outro controle e thisControl.updateValueAndValidity() para acionar outra rodada de validação quando outro controle é alterado.


Estou copiando um código abaixo para referência 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

Veja como você pode usá-lo com formulários reativos:

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

Validadores mais atualizados podem ser encontrados aqui: moebius-mlm / ng-validators .

Para expandir a resposta de matthewdaniel, uma vez que não está exatamente correto. Aqui está um código de exemplo que mostra como atribuir corretamente um validador a um 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 }; } }

Veja um exemplo prático : http://plnkr.co/edit/Zcbg2T3tOxYmhxs7vaAm?p=preview

Muita escavação na fonte angular, mas encontrei uma maneira melhor.

 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 }; } 

Porção HTML para o grupo de senhas

 
Passwords must match.

Aqui está uma outra opção que eu consegui criar, que não depende de um ControlGroup inteiro ou de um subgrupo, mas está vinculada diretamente a cada Control .

O problema que tive foi que os controles que dependiam uns dos outros não estavam hierarquicamente juntos, então não consegui criar um ControlGroup . Além disso, meu CSS foi configurado para que cada controle aproveitasse as classs angulares existentes para determinar se exibia um estilo de erro, o que era mais complicado ao lidar com uma validação de grupo em vez de uma validação específica de controle. Tentar determinar se um único controle era válido não era possível, pois a validação estava vinculada ao grupo de controles e não a cada controle individual.

No meu caso, eu queria um valor de checkbox de seleção para determinar se outro campo seria necessário ou não.

Isso é criado usando o Form Builder no componente. Para o modelo select em vez de vinculá-lo diretamente ao valor do object request, vinculei-o a funções get / set que permitirão que eu manipule events “on change” para o controle. Então, poderei definir manualmente a validação para outro controle, dependendo do novo controle de seleção.

Aqui está a parte relevante da vista:

  ...  

A parte 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(); } } } 

No meu caso eu sempre tive uma validação de padrão vinculada ao controle, então o validator está sempre configurado para algo, mas eu acho que você pode definir o validator para null se você não tiver nenhuma validação vinculada ao controle.

UPDATE: Existem outros methods de capturar a mudança de modelo como (ngModelChange)=changeFunctionName($event) ou assinar alterações de controle de valor usando this.form.controls["employee"].valueChanges.subscribe(data => ...))

Eu tentei a maioria dessas respostas, mas nenhuma delas funcionou para mim. Eu encontrei um exemplo de trabalho aqui https://scotch.io/@ibrahimalsurkhi/match-password-validation-with-angular-2

Estava procurando por isso também e acabei usando o equalTo do pacote de validação ng2 ( https://www.npmjs.com/package/ng2-validation )

Aqui está um exemplo: Template Driven:

  

required error

equalTo error

Modelo dirigido:

 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

Aqui está minha versão que usei para garantir que uma idade em um campo seja maior ou igual à idade em outro campo. Estou usando grupos de formulários também, então eu uso a function group.get vez 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 }) } } } 

E no 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') }); 

Eu acho que sua melhor aposta, por enquanto, é criar um grupo de formulários para manter seus controles. Quando você instancia seu passe de controle na function para validá-lo. exemplo:

  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; }); 

Eu sei que isso é altamente dependente da versão do angularjs2 que você está executando. Isso foi testado contra 2.0.0-alpha.

Se alguém tiver uma sugestão melhor, como escrever um validador personalizado (que pode ser o melhor caminho a seguir), será bem-vindo.

EDITAR

você também pode usar o ControlGroup e validar esse grupo integralmente.

 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; }); 

Basta editar as mensagens de acordo com seu domínio.

A resposta de Louis Cruz foi muito útil para mim.

Para completar basta adicionar no else o setErrors reset: return passwordConfirmationInput.setErrors (null);

E tudo funciona bem!

Te agradece,

Saudações,

TGA

Eu sugeriria usar a biblioteca ng-form-rules . É uma biblioteca incrível para criar todos os diferentes tipos de formulários com lógica de validação desacoplada do componente e que pode depender de alterações de valor de outras áreas no formulário. Eles têm ótima documentação , exemplos e um vídeo que mostra um monte de sua funcionalidade . Fazer validação assim, o que você está tentando fazer é trivial.

Você pode conferir o README para algumas informações de alto nível e um exemplo básico.

Regras de validação de correspondência de senha angular 4.

Se você precisar de campos de controle de erros, então você pode fazê-lo.

 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')}); } 

Então você precisa declarar isto neste método no método constructor .

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

Em vez de definir um erro no ControlGroup, faça isso no campo real da seguinte forma:

  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 }; } } } 

Porção HTML para o grupo de senhas

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