Search code examples
angularangular-reactive-forms

Angular Reactive form weird behavior


I have this code I'm working on:

login.component.html

<div class="main">
    <div class="image">
        <img src="./assets/icons/login.png" alt="Login">
        <p>Login in to your account</p>
    </div>
        <form [formGroup]="form" class="login" (ngSubmit)="onClick()">
            <p class="error-message" style="font-size: large;" *ngIf="!isValid">Please fill the your info</p>
            <p>Enter your user name and password</p>
            <div class="userName">
                <label for="userName">User name</label>
                <br>
                <input [ngClass]="{'error': emailValidation && !isValid}" type="text" id="userName" placeholder="Your user name" formControlName="email">
                <p class="error-message" *ngIf="emailValidation && !isValid">please enter your user name</p>
            </div>
            <div class="pass">
                <label for="pass">Password</label>
                <br>
                <input [ngClass]="{'error': passwordValidation || !isValid}" type="password" id="pass" placeholder="your password" formControlName="password">
                <p class="error-message" *ngIf="passwordValidation || !isValid">please enter your user password</p>
            </div>
            <button class="btnConfig" id="btnlogin"><img src="./assets/icons/login-btn.png" alt="login"> Login</button>
        </form>             
</div>

login.component.ts:

import { AfterContentChecked, Component, DoCheck, OnChanges, OnInit, SimpleChanges } from '@angular/core';
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
import {  Router } from '@angular/router';

@Component({
  selector: 'app-login',
  templateUrl: './login.component.html',
  styleUrls: ['./login.component.css']
})
export class LoginComponent implements OnInit, DoCheck {
  form!: FormGroup;
  isValid: boolean = true;

  constructor (private formBuilder: FormBuilder, private router: Router) {}

  ngOnInit(): void {
    this.form = this.formBuilder.group({
      email: ['', Validators.required],
      password: ['', Validators.required]
    });

    this.form.valueChanges.subscribe()
  }


  ngDoCheck(): void {
    this.form.valueChanges.subscribe(() => {
      this.isValid = this.form.valid;
    });  
  }

  get emailValidation() {
    return this.form.get('email')?.invalid || this.form.get('email')?.touched;
  }

  get passwordValidation() {
    return this.form.get('password')?.invalid && this.form.get('password')?.touched;
  }


  onClick() {
    if(this.form.valid) {
      this.router.navigate(['/dashboard']);
    } else {
      this.isValid = this.form.valid;
    }
  }
}

Live preview

Everything works fine with the validation until I press the login button with the two input fields empty the error message shows. And when I just fill one of the input the error messages disappears from both fields.

I tried

ngDoCheck(): void {
    this.form.valueChanges.subscribe(() => {
      this.isValid = this.form.valid;
    });  
  }

to keep checking but still the bug doesn't go.


Solution

  • i fix the problem by remove the ngDoCheck, put the subscription in to ngOnInit, replace the isVaild with isSubmitted, edit the passwordValidation and emailValidation, and edit the OnClick to be onSubmit

    login.component.ts:

        import { AfterContentChecked, Component, DoCheck, OnChanges, OnInit, SimpleChanges } from '@angular/core';
    import { FormGroup, FormBuilder, Validators } from '@angular/forms';
    import {  Router } from '@angular/router';
    
    @Component({
      selector: 'app-login',
      templateUrl: './login.component.html',
      styleUrls: ['./login.component.css']
    })
    export class LoginComponent implements OnInit {
      form!: FormGroup;
      isSubmitted: boolean = false;
    
      constructor (private formBuilder: FormBuilder, private router: Router) {}
    
      ngOnInit(): void {
        this.form = this.formBuilder.group({
          email: ['', Validators.required],
          password: ['', Validators.required]
        });
    
        this.form.valueChanges.subscribe()
      }
    
      get emailValidation() {
        return this.form.get('email')?.invalid && (this.form.get('email')?.dirty || this.form.get('email')?.touched || this.isSubmitted);
      }
    
      get passwordValidation() {
        return this.form.get('password')?.invalid && (this.form.get('password')?.dirty || this.form.get('password')?.touched || this.isSubmitted);
      }
    
    
      onSubmit(): void {
        if(this.form.valid){
          this.router.navigate(['/dashboard'])
        }
        this.isSubmitted = true;
      }
    }
    

    login.component.html:

    <div class="main">
        <div class="image">
            <img src="./assets/icons/login.png" alt="Login">
            <p>Login in to your account</p>
        </div>
            <form [formGroup]="form" class="login" (ngSubmit)="onSubmit()">
                <p class="error-message" style="font-size: large;" *ngIf="isSubmitted">Please fill the your info</p>
                <p>Enter your user name and password</p>
                <div class="userName">
                    <label for="userName">User name</label>
                    <br>
                    <input [ngClass]="{'error': emailValidation}" type="text" id="userName" placeholder="Your user name" formControlName="email">
                    <p class="error-message" *ngIf="emailValidation">please enter your user name</p>
                </div>
                <div class="pass">
                    <label for="pass">Password</label>
                    <br>
                    <input [ngClass]="{'error': passwordValidation}" type="password" id="pass" placeholder="your password" formControlName="password">
                    <p class="error-message" *ngIf="passwordValidation">please enter your user password</p>
                </div>
                <button class="btnConfig" id="btnlogin" type="submit"><img src="./assets/icons/login-btn.png" alt="login"> Login</button>
            </form>             
    </div>