Solucionado (ver solução)
Solucionado
(ver solução)
13
respostas

Mesmos problema de vários posts e ainda não solucionado

Vi diversos posts com um desses problemas, mas nenhum deles solucionou o meu caso.

Apresenta primeiro o erro abaixo:

ERROR TypeError: Cannot read properties of undefined (reading 'pipe')
    at usuario-existe.service.ts:15:35
    at forms.js:1190:1
    at Array.map (<anonymous>)
    at executeValidators (forms.js:1190:1)
    at FormControl._composedAsyncValidatorFn (forms.js:1165:1)
    at FormControl._runAsyncValidator (forms.js:3057:1)
    at FormControl.updateValueAndValidity (forms.js:3031:1)
    at new FormControl (forms.js:3388:1)
    at FormBuilder.control (forms.js:6731:1)
    at FormBuilder._createControl (forms.js:6769:1)

Daí edito a linha abaixo colocando o ? e o erro muda para o que vem a seguir:

      return control.valueChanges?.pipe(

Erro alterado:

core.js:4442 ERROR Error: Expected validator to return Promise or Observable.
    at toObservable (forms.js:1176:1)
    at Array.map (<anonymous>)
    at FormControl._composedAsyncValidatorFn (forms.js:1165:1)
    at FormControl._runAsyncValidator (forms.js:3057:1)
    at FormControl.updateValueAndValidity (forms.js:3031:1)
    at new FormControl (forms.js:3388:1)
    at FormBuilder.control (forms.js:6731:1)
    at FormBuilder._createControl (forms.js:6769:1)
    at forms.js:6755:1
    at Array.forEach (<anonymous>)

Abaixo segue o arquivo usuario-existe.service.ts

import { AbstractControl } from '@angular/forms';
import { NovoUsuarioService } from './novo-usuario.service';
import { Injectable } from '@angular/core';
import { first, map, switchMap } from 'rxjs/operators';

@Injectable({
  providedIn: 'root'
})
export class UsuarioExisteService {

  constructor(private novoUsuarioService: NovoUsuarioService) { }

  usuarioExiste(){
    return (control: AbstractControl) => {
      return control.valueChanges?.pipe(
        switchMap(
          (nomeUsuario) => this.novoUsuarioService.verificaUsuarioExistente(nomeUsuario)
        ),
        map(
          (usuarioExiste) => usuarioExiste?{ usuarioExistente: true }:null
        ),
        first()
      );
    }
  }
}

Segue o arquivo novo-usuario.component.ts

import { UsuarioExisteService } from './usuario-existe.service';
import { Router } from '@angular/router';
import { NovoUsuarioService } from './novo-usuario.service';
import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { NovoUsuario } from './novo-usuario';
import { minusculoValidator } from './minusculo.validator';

@Component({
  selector: 'app-novo-usuario',
  templateUrl: './novo-usuario.component.html',
  styleUrls: ['./novo-usuario.component.css']
})
export class NovoUsuarioComponent implements OnInit {

  novoUsuarioForm!: FormGroup;

  constructor(
    private formBuilder: FormBuilder,
    private userService: NovoUsuarioService,
    private router: Router,
    private usuarioExiste: UsuarioExisteService
  ) { }

  ngOnInit(): void {
    this.novoUsuarioForm = this.formBuilder.group({
      email: ['', [Validators.required, Validators.email]],
      fullName: ['', [Validators.required, Validators.minLength(4)]],
      userName: [
        '',
        [minusculoValidator],
        [this.usuarioExiste.usuarioExiste()]
      ],
      password: ['']
    })
  }

  cadastrar(){
    const novoUsuario = this.novoUsuarioForm.getRawValue() as NovoUsuario;
    console.log(novoUsuario);
    this.userService.cadastraNovoUsuario(novoUsuario)
      .subscribe(
        () => {
          this.router.navigate(['']);
        },
        (error) => {
          alert('Erro no cadastro de novo usuário.');
          console.error(error)
        }
      );
  }

}
13 respostas

Oi Marcus,

Acho que pode ser resolvido da seguinte maneira (não testado):

Trecho de usuario-existe.service.ts

usuarioExiste(control: AbstractControl){
      return control.valueChanges?.pipe(
        switchMap(
          (nomeUsuario) => this.novoUsuarioService.verificaUsuarioExistente(nomeUsuario)
        ),
        map(
          (usuarioExiste) => usuarioExiste?{ usuarioExistente: true }:null
        ),
        first()
      );

  }

Trecho de novo-usuario.component.ts

ngOnInit(): void {
    this.novoUsuarioForm = this.formBuilder.group({
      email: ['', [Validators.required, Validators.email]],
      fullName: ['', [Validators.required, Validators.minLength(4)]],
      userName: [
        '',
        [minusculoValidator],
        [this.usuarioExiste.usuarioExiste] // Repare que aqui fiz a remoção dos parênteses
      ],
      password: ['']
    })
  }

Espero que isso ajude!

Obrigado pela ajuda Jonas, mas infelizmente a sugestão não funcionou. Agora apresenta outro erro:

ERROR Error: Expected validator to return Promise or Observable.
    at toObservable (forms.js:1176:1)
    at Array.map (<anonymous>)
    at FormControl._composedAsyncValidatorFn (forms.js:1165:1)
    at FormControl._runAsyncValidator (forms.js:3057:1)
    at FormControl.updateValueAndValidity (forms.js:3031:1)
    at new FormControl (forms.js:3388:1)
    at FormBuilder.control (forms.js:6731:1)
    at FormBuilder._createControl (forms.js:6769:1)
    at forms.js:6755:1
    at Array.forEach (<anonymous>)
defaultErrorLogger @ core.js:4442
handleError @ core.js:4490
(anonymous) @ core.js:28482
invoke @ zone-evergreen.js:364
run @ zone-evergreen.js:123
runOutsideAngular @ core.js:27485
tick @ core.js:28482
(anonymous) @ core.js:28361
invoke @ zone-evergreen.js:364
onInvoke @ core.js:27558
invoke @ zone-evergreen.js:363
run @ zone-evergreen.js:123
run @ core.js:27440
next @ core.js:28360
schedulerFn @ core.js:24923
__tryOrUnsub @ Subscriber.js:183
next @ Subscriber.js:122
_next @ Subscriber.js:72
next @ Subscriber.js:49
next @ Subject.js:39
emit @ core.js:24913
checkStable @ core.js:27494
onHasTask @ core.js:27572
hasTask @ zone-evergreen.js:419
_updateTaskCount @ zone-evergreen.js:440
_updateTaskCount @ zone-evergreen.js:263
runTask @ zone-evergreen.js:184
drainMicroTaskQueue @ zone-evergreen.js:569
Promise.then (async)
scheduleMicroTask @ zone-evergreen.js:552
scheduleTask @ zone-evergreen.js:388
onScheduleTask @ zone-evergreen.js:272
scheduleTask @ zone-evergreen.js:378
scheduleTask @ zone-evergreen.js:210
scheduleMicroTask @ zone-evergreen.js:230
scheduleResolveOrReject @ zone-evergreen.js:847
resolvePromise @ zone-evergreen.js:785
(anonymous) @ zone-evergreen.js:705
webpackJsonpCallback @ bootstrap:25
(anonymous) @ home-home-module.js:1
core.js:4442 ERROR Error: formGroup expects a FormGroup instance. Please pass one in.

       Example:


    <div [formGroup]="myGroup">
      <input formControlName="firstName">
    </div>

    In your class:

    this.myGroup = new FormGroup({
       firstName: new FormControl()
    });
    at Function.missingFormException (forms.js:1700:1)
    at FormGroupDirective._checkFormPresent (forms.js:5632:1)
    at FormGroupDirective.ngOnChanges (forms.js:5454:1)
    at FormGroupDirective.rememberChangeHistoryAndInvokeOnChangesHook (core.js:2373:1)
    at callHook (core.js:3285:1)
    at callHooks (core.js:3251:1)
    at executeInitAndCheckHooks (core.js:3203:1)
    at selectIndexInternal (core.js:6324:1)
    at Module.ɵɵadvance (core.js:6306:1)
    at NovoUsuarioComponent_Template (novo-usuario.component.html:7:7)

Tbm estou com o mesmo problema. Alguém da Alura pra ajudar?

Marcus, meu código está errado mesmo. Desculpe, quando respondi não testei, mas agora estou com o código da aula preparado aqui.

Como não estou fazendo esse curso no momento, para conseguir ter o código mais próximo possível do seu, baixei o projeto através do link https://github.com/alura-cursos/angular_formularios/archive/aula_4.zip

Passos que segui: 1) instalei frontend e api 2) rodei frontend e api 3) fui ao formulário de cadastro de usuarios 4) digitei alguns valores no campo "Usuário" 5) não obtive o erro

Mas está dando erro para você e mais pessoas, então substituí alguns trechos pelo seu código (tudo o que você mencionou de código aqui), mas também não deu erro. Imagino que a gente ainda não olhou pra causa real do erro.

Você poderia disponibilizar o seu código completo num github (frontend + api) ou algo do tipo para que eu possa rodar ele?

Jonas, você acredita que a versão do angular possa causar este erro? Pois a única diferença é que estou usando a versão 10.1 e não a 11, utilizada pelo instrutor.

Segue o projeto

https://github.com/marcusrsantos/gatitobook

Oi Marcus, seu projeto não aparece aqui pra mim, dá 404 not found. Ele está privado?

Ângelo, é importante estar rodando a mesma versão para evitar imprevistos, porém esse recurso em específico não creio que tenha sofrido alterações de um tempo pra cá. Ao menos do Angular 8 até o 13, o recurso de async validator permanece da mesma forma.

Desculpa Jonas, acabei criando o repositório como privado realmente. Já mudei a visibilidade para público. Veja se consegue agora.

solução!

Marcus A implementação que você fez está em conformidade com o projeto do curso e eu obtinha exatamente o erro que você relatou. Mesmo copiando os trechos de implementações do projeto "oficial" do curso para dentro do seu projeto, não funcionava. O inverso também ocorria. Se eu copiasse trechos do seu código para dentro do projeto "oficial" do curso, funcionava.

A impressão que tive é que o FormBuilder não esta funcionando de acordo na versão que seu projeto estava, pois substituindo o uso de FormBuilder pela construção manual usando new FormGroup(...) passou a funcionar.

O que fiz para funcionar SEM ALTERAR a versão do Angular:

arquivo: minusculo.validator.ts (trecho)

export function minusculoValidator(control: AbstractControl) {
  const valor = control.value as string;

  if (!!valor && valor !== valor.toLowerCase()) {
    return { minusculo: true };
  } else {
    return null;
  }
}

arquivo: usuario-existe.service.ts (trecho)

export class UsuarioExisteService {
  constructor(private novoUsuarioService: NovoUsuarioService) {}

  usuarioExiste() {
    return (control: AbstractControl) => {
      if (control && control.valueChanges) {
        return control.valueChanges.pipe(
          switchMap((nomeUsuario) =>
            this.novoUsuarioService.verificaUsuarioExistente(nomeUsuario)
          ),
          map((usuarioExiste) =>
            usuarioExiste ? { usuarioExistente: true } : null
          ),
          first()
        );
      } else {
        return of(null);
      }
    };
  }
}

Alterando os dois arquivos acima pude executar o código normalmente sem ter mais aqueles erros.

O que fiz para funcionar ALTERANDO a versão do Angular:

Nessa abordagem não precisa alterar sua implementação, apenas atualizar o Angular. Após atualizado tudo passará a funcionar, ou seja, o mesmo código que dá erro antes da atualização, passará a funcionar após a atualização.

1) Dentro do arquivo gatitobook/package.json substitua as dependencies e devDependencies deixando assim:

  "dependencies": {
    "@angular/animations": "~11.0.0",
    "@angular/common": "~11.0.0",
    "@angular/compiler": "~11.0.0",
    "@angular/core": "~11.0.0",
    "@angular/forms": "~11.0.0",
    "@angular/platform-browser": "~11.0.0",
    "@angular/platform-browser-dynamic": "~11.0.0",
    "@angular/router": "~11.0.0",
    "bootstrap": "^4.5.3",
    "font-awesome": "^4.7.0",
    "rxjs": "~6.6.0",
    "tslib": "^2.0.0",
    "zone.js": "~0.10.2"
  },
  "devDependencies": {
    "@angular-devkit/build-angular": "~0.1100.0",
    "@angular/cli": "~11.0.0",
    "@angular/compiler-cli": "~11.0.0",
    "@types/jasmine": "~3.6.0",
    "@types/node": "^12.11.1",
    "codelyzer": "^6.0.0",
    "jasmine-core": "^3.8.0",
    "jasmine-spec-reporter": "~5.0.0",
    "karma": "~5.1.0",
    "karma-chrome-launcher": "~3.1.0",
    "karma-coverage": "~2.0.3",
    "karma-jasmine": "~4.0.0",
    "karma-jasmine-html-reporter": "^1.5.0",
    "protractor": "~7.0.0",
    "ts-node": "~8.3.0",
    "tslint": "~6.1.0",
    "typescript": "~4.0.2"
  }

2) Apague seu arquivo package-lock.json e seu diretório node_modules;

3) Efetue npm install;

4) Rode o projeto com ng serve;


Concluindo Apresentei uma forma de resolver esse problema através de alteração na implementação, mas não acho vantagem tratar assim. Melhor avançar para a versão 11 do Angular e ser feliz.

Então confirmando o questionamento do Ângelo: isso não ocorre na versão 11 do Angular

Cara valeu! Resolveu aqui. Não entendi ao certo o que houve, mas pelo que vc disse, deve ser algum problema do FormBuilder com a versão 10 do angular. Muito obrigado e muito sucesso!

Obrigado pela ajuda Jonas! Realmente atualizando a versão do angular para a 11 funcionou sim. Voltei para procurar nos arquivos do projeto e em nenhum vi a versão do angular usada, mas vi apenas no segundo vídeo, quando ele executa a instalação do angular-cli, ele não coloca versão, mas automaticamente instala a 11. Acho que um erro desse, onde não se menciona a versão que deve ser usada no curso, deveria ser dada uma melhor atenção e atualizar o curso para os demais não sofrerem. Vlw a disposição e, pelo visto, foi algo que foi sanado em novas versões do angular, ou seja, você foçou o retorno de nulo, caso não existisse o control ou não tivesse alterações no mesmo. Parece que nas novas versões já se faz isso sem que precisemos colocar no código.

Isso mesmo. Parece que na versão que vocês estavam utilizando, o formulário é iniciado antes dele estar "pronto para trabalhar".

Ufa! Resolvido!