Como encontrar os controles inválidos na forma reativa angular 4

92

Eu tenho uma forma reativa em Angular como abaixo:

this.AddCustomerForm = this.formBuilder.group({
    Firstname: ['', Validators.required],
    Lastname: ['', Validators.required],
    Email: ['', Validators.required, Validators.pattern(this.EMAIL_REGEX)],
    Picture: [''],
    Username: ['', Validators.required],
    Password: ['', Validators.required],
    Address: ['', Validators.required],
    Postcode: ['', Validators.required],
    City: ['', Validators.required],
    Country: ['', Validators.required]
});

createCustomer(currentCustomer: Customer) 
{
    if (!this.AddCustomerForm.valid)
    {
        //some app logic
    }
}

this.AddCustomerForm.valid retorna falso, mas tudo parece bom.

Eu tentei descobrir com a verificação da propriedade de status na coleção de controles. Mas eu me pergunto se existe uma maneira de localizar os inválidos e exibi-los ao usuário?

sa_
fonte
Se você deseja apenas exibir os campos com erro, pode usar css para destacar ou colorir os campos inválidos. Cada campo inválido tem uma classe "ng-invalid" anexada em sua lista de classes
LookForAngular

Respostas:

176

Você pode simplesmente iterar em cada controle e verificar o status:

    public findInvalidControls() {
        const invalid = [];
        const controls = this.AddCustomerForm.controls;
        for (const name in controls) {
            if (controls[name].invalid) {
                invalid.push(name);
            }
        }
        return invalid;
    }
Max Koretskyi
fonte
1
obrigado por isso mas tentei fazer isso e mesmo isso não retorna nada meu formulário ainda é inválido, isso é estranho. Quer dizer, este código parece bom, mas não faz sentido porque form.valid retorna falso
sa_
o que findInvalidControls()você retorna?
Max Koretskyi
1
não retorna nada, inválido está vazio. Eu verifiquei um por um na tela de visualização de depuração, todos os controles são válidos, mas this.AddCustomerForm.valid ainda retorna falso.
sa_
Eu acho que descobri. há um campo de e-mail e uma expressão regular, mas de alguma forma o status do controle é PENDENTE e pode ser a causa
sa_
7
@ AngularInDepth.com - se um dos controles for um grupo de formulários, sua função retornará o grupo de formulários inválido e não o controle de formulário específico que é inválido
john Smith
36

Acabei de lutar contra este problema: cada campo do formulário é válido, mas ainda assim o próprio formulário é inválido.

Acontece que eu tinha definido 'Validator.required' em um FormArray onde os controles são adicionados / removidos dinamicamente. Portanto, mesmo que o FormArray estivesse vazio, ele ainda era necessário e, portanto, o formulário sempre era inválido, mesmo se todos os controles visíveis fossem preenchidos corretamente.

Eu não encontrei a parte inválida do formulário, porque minha função 'findInvalidControls' verificou apenas FormControl's e não FormGroup / FormArray. Então eu atualizei um pouco:

/* 
   Returns an array of invalid control/group names, or a zero-length array if 
   no invalid controls/groups where found 
*/
public findInvalidControlsRecursive(formToInvestigate:FormGroup|FormArray):string[] {
    var invalidControls:string[] = [];
    let recursiveFunc = (form:FormGroup|FormArray) => {
      Object.keys(form.controls).forEach(field => { 
        const control = form.get(field);
        if (control.invalid) invalidControls.push(field);
        if (control instanceof FormGroup) {
          recursiveFunc(control);
        } else if (control instanceof FormArray) {
          recursiveFunc(control);
        }        
      });
    }
    recursiveFunc(formToInvestigate);
    return invalidControls;
  }
Jette
fonte
3
Resposta incrivelmente útil. Muito obrigado
Mikki
1
Concordo, uma resposta muito útil.
nenea
24

Um controle Angular inválido possui a classe CSS chamada 'ng-invalid' .

Em DevTools no Chrome, selecione a guia Console.

No prompt do console, digite o comando:

document.getElementsByClassName('ng-invalid')

A saída deve ser semelhante a esta: insira a descrição da imagem aqui

Nesse caso, o texto sublinhado é para o controle do formulário listen-address. E o texto circulado: .ng-invalidindica que o controle é inválido.

Nota: Testado em cromo

Mwiza
fonte
2
esta parece ser a forma mais direta de responder à pergunta.
ckapilla
2
Você me salvou de enlouquecer, se eu pudesse
pagar
3

Os formulários e todos os seus controles estendem a classe angular AbstractControl. Cada implementação possui um acessador para os erros de validação.

let errors = this.AddCustomerForm.errors
// errors is an instance of ValidatorErrors

A documentação da API contém todas as referências https://angular.io/api/forms/AbstractControl

Editar

Achei que o acessador de erro funcionasse dessa maneira, no entanto, este link para o github mostra que há algumas outras pessoas que pensaram o mesmo que eu https://github.com/angular/angular/issues/11530

Em qualquer caso, usando o acessador de controles, você pode iterar sobre todos os formControls em seu formulário.

Object.keys(this.AddCustomerForm.controls)
    .forEach( control => {
        //check each control here
        // if the child is a formGroup or a formArray
        // you may cast it and check it's subcontrols too
     })
LookForAngular
fonte
1
isso retorna nulo mesmo que haja controles vazios
sa_
1
Ele deve retornar nulo quando não houver erros. Você pode postar seu modelo?
LookForAngular de
Sim, isso não vai funcionar, as diferentes validações definidas em cada controle de formulário, esses controles de cada formulário contêm seus erros, o formulário não. Você precisa iterar os controles como o Maximus deu a resposta.
AJT82 de
Posso acessar erros para cada contorls individual como this.form.controls ['Email'].
Errors
@ AJT_82 de fato, o próprio formulário pode mostrar erros se um validador tiver sido definido para o formGroup (verifique os documentos sobre validação de campo cruzado, o que faz sentido validar no grupo e não no controle)
LookForAngular
3

Agora, no angular 9, você pode usar o método markAllAsTouched () para mostrar os validadores de controles inválidos:

this.AddCustomerForm.markAllAsTouched();
Rodrigo Jorge Baptista
fonte
Vou dar +1, pois me ajudou a descobrir o que eu precisava saber --- que é mostrar mensagens de validação quando o usuário não necessariamente tocou nas entradas.
Sean Halls
1

Se você não tiver muitos campos no formulário, pode simplesmente F12 e passar o mouse sobre o controle, você poderá ver o pop-up com os valores pristine / Touch / valid do campo- "# fieldname.form-control.ng- untouched.ng-invalid ".

darshna mishra
fonte
1

Acho que você deve tentar usar this.form.updateValueAndValidity()ou tentar executar o mesmo método em cada um dos controles.

Javi Marzán
fonte
1

tente isso

 findInvalidControls(f: FormGroup) {
    const invalid = [];
    const controls = f.controls;
    for (const name in controls) {
      if (controls[name].invalid) {
        invalid.push(name);
      }
    }
    return invalid;
  }
Trilok Singh
fonte
1

Isso irá registrar todos os nomes dos controles 😊

for (let el in this.ReactiveForm.controls) {
      if (this.ReactiveForm.controls[el].errors) {
        console.log(el)
      }
 }          

você pode fazer uma matriz ou string com isso e exibir para o usuário

tejas n
fonte
0

Tomei a liberdade de melhorar o código do AngularInDepth.com , de modo que ele também procure entradas inválidas em formulários aninhados de maneira recursiva. Seja aninhado por FormArray-s ou FormGroup-s. Basta inserir o formGroup de nível superior e ele retornará todos os FormControls que são inválidos.

Você pode possivelmente ignorar algumas das verificações do tipo "instanceof", se separar a verificação do FormControl e a adição à funcionalidade de array inválido em uma função separada. Isso faria com que a função parecesse muito mais limpa, mas eu precisava de uma opção de função única e global para obter um array simples de todos os formControls inválidos e esta é a solução!

findInvalidControls( _input: AbstractControl, _invalidControls: AbstractControl[] ): AbstractControl[] {
    if ( ! _invalidControls ) _invalidControls = [];
    if ( _input instanceof FormControl  ) {
        if ( _input.invalid ) _invalidControls.push( _input );
        return _invalidControls;
    }

    if ( ! (_input instanceof FormArray) && ! (_input instanceof FormGroup) ) return _invalidControls;

    const controls = _input.controls;
    for (const name in controls) {
        let control = controls[name];
        switch( control.constructor.name )
        {
            case 'AbstractControl':
            case 'FormControl':
                if (control.invalid) _invalidControls.push( control );
                break;

            case 'FormArray':
                (<FormArray> control ).controls.forEach( _control => _invalidControls = findInvalidControls( _control, _invalidControls ) );
                break;

            case 'FormGroup':
                _invalidControls = findInvalidControls( control, _invalidControls );
                break;
        }
    }

    return _invalidControls;
}

Apenas para aqueles que precisam, para que não tenham que codificar sozinhos.

Editar # 1

Foi solicitado que ele também retornasse FormArray-s e FormGroups inválidos, então se você também precisar disso, use este código

findInvalidControls( _input: AbstractControl, _invalidControls: AbstractControl[] ): AbstractControl[] {
    if ( ! _invalidControls ) _invalidControls = [];
    if ( _input instanceof FormControl  ) {
        if ( _input.invalid ) _invalidControls.push( _input );
        return _invalidControls;
    }

    if ( ! (_input instanceof FormArray) && ! (_input instanceof FormGroup) ) return _invalidControls;

    const controls = _input.controls;
    for (const name in controls) {
        let control = controls[name];
        if (control.invalid) _invalidControls.push( control );
        switch( control.constructor.name )
        {    
            case 'FormArray':
                (<FormArray> control ).controls.forEach( _control => _invalidControls = findInvalidControls( _control, _invalidControls ) );
                break;

            case 'FormGroup':
                _invalidControls = findInvalidControls( control, _invalidControls );
                break;
        }
    }

    return _invalidControls;
}
Karl Johan Vallner
fonte
1
Eu tentei, mas não encontrou nenhum FormGroup ou FormArray inválido ... apenas FormControl inválido. Eu cometi o mesmo erro ... veja minha resposta.
Jette
Melhorei minha resposta para se adequar ao seu caso de uso.
Karl Johan Vallner
0

você pode registrar o valor do formulário console.log(this.addCustomerForm.value), ele consolará todos os valores do controle e os campos nulos ou "" (vazio) indicam controles inválidos

Sohail Anwar
fonte
0

Se quiser verificar se o formulário é válido ou não e não quiser fazer nenhuma alteração no código, você pode tentar executar o seguinte comando no console da ferramenta de desenvolvedor do Chrome. Certifique-se de que seu aplicativo angular esteja exibindo o componente que hospeda o formulário em questão.

ng.probe(document.querySelector("app-your-component-selector-name")).componentInstance;

Não se esqueça de substituir o nome do seletor de componente no comando acima.

Este comando irá listar todas as variáveis ​​de seu componente, incluindo AddCustomerForm. Agora, se você expandir isso, verá uma lista de todos os seus controles. Você pode então expandir cada controle para verificar se ele é válido ou não.

RITZ XAVI
fonte
-1

Verifique o valor de controle de formulário vazio ou nulo na página html

Valor dos controles do formulário: {{formname.value | json}}
Uttam Kar
fonte