Angular displaying flash message after failed login

Viewed 593

I am building an angular application in Angular 9 and i am trying to display a flash message when the login fails, i don't understand what i am missing.

This is the LoginComponent:

import { Component, OnInit, ViewChild, ElementRef } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms';
import { AuthService } from '../services/auth.service';
import { Router } from '@angular/router';

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

  loginForm: FormGroup;
  state;
  message: any;

  constructor(private formBuilder: FormBuilder, private authenticationService: AuthService, private router: Router) {
    this.state = this.router.getCurrentNavigation().extras.state;
    if(authenticationService.isLoggedIn()){
      this.router.navigateByUrl('');
    }
  }

  ngOnInit(): void {
    this.loginForm = this.formBuilder.group({
      email: '',
      userpass: ''
    });
  }

  onSubmit(credentials){
    this.authenticationService.login(credentials).subscribe((res: any) => {
      console.log(res);
      if(res.token != null){
        localStorage.setItem('token', res.token);
        this.router.navigateByUrl('/');
      } else {
        console.log("Wrong email or password");
        this.message = "Wrong email or password";
      }
    });
  }

}

This is the LoginComponent html:

<div class="hero is-large is-success" align="center">
    <div class="hero-head">
        <h2 class="title">Login</h2>
        <div *ngIf="message != null" class="notification">
            <p>{{ message }}</p>
        </div>

        <form class="is-centered" [formGroup]="loginForm" (ngSubmit)="onSubmit(loginForm.value)">
            <div class="container">
                <input class="form-control input is-rounded is-dark" type="text" formControlName="email" placeholder="email"/><br>
                <input class="form-control input is-rounded is-dark" type="password" formControlName="userpass" placeholder="password"/><br>
                <input type="submit" class="button is-rounded is-dark" value="Login">
            </div>
        </form>
    </div>
    <div class="hero-body">

    </div>
</div>

The AuthenticationService:

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Appdata } from '../appdata';
import { Router } from '@angular/router';

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

  authUrl: string;

  constructor(private http: HttpClient, private router: Router) {
    this.authUrl = Appdata.url + "/auth";
  }

  login(credentials) {
    return this.http.post<any>(this.authUrl+'/login', credentials).subscribe((res: any) => {
      console.log(res);
      if(res.token != null){
        localStorage.setItem('token', res.token);
        this.router.navigateByUrl('/');
      } else {
        this.router.navigate(['/login', { state: res.status }]);
      }
    });
  }

  register(user) {
    return this.http.post<any>(Appdata.url+'/register', user).subscribe((res: any) =>{
      if(res.redirect != null){
        this.router.navigateByUrl('/login');
      } else {
        this.router.navigate(['/register', { state: res.status }]);
      }
    });
  }

  unregister(credentials) {
    return this.http.post<any>(Appdata.url+'/unregister', credentials).subscribe((res: any) => {
      if(res.redirect != null){
        this.router.navigateByUrl('/');
      } else {
        this.router.navigate(['/unregister', { state: res.status }]);
      }
    });
  }

  logout(){
    if(this.isLoggedIn()){
      localStorage.removeItem('token');
    }
  }

  isLoggedIn(){
    return localStorage.getItem('token') != null;
  }

}

One thing to notice is that when the 401 status is reached it goes to the correct url (which is: http://localhost:4200/login;state=401) but it doesn't log the message and it doesn't display the message in the html page.

I have already tried using angular2-flash-messages and ng-flash-messages module but they don't seem to do the job.

1 Answers

The problem you're having is that you're always subscribing and you're only getting the "next" handler AKA the handler that will trigger when things go well.

Essentially what you want to do is add the error function to do extra handling over there since the Angular HTTP client sends the HTTP error codes over that function.

Your typescript component could look something like the following:

@Component({...})
export class LoginComponent implements OnInit {

  message : string = ''

  constructor(private auth: AuthService) {}

  ngOnInit() {}
  
  onSubmit() {
    this.authenticationService.login(credentials)
      .subscribe(
         (res: any) => {
           localStorage.setItem('token', res.token);
           this.router.navigateByUrl('/');
         }, 
         (error: any) => {
           // if your server responds with a 4XX or 5XX status code
           // this error handler will be called.
           // here you can have more logic than this
           this.message = 'Invalid username or password'
         }
      }
    });
  }

}

And then make sure you're returning the Observable and NOT the subscription:

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

  constructor(private http: HttpClient, private router: Router) {
    this.authUrl = Appdata.url + "/auth";
  }

  login(credentials): Observable<any> {
    return this.http.post<any>(this.authUrl+'/login', credentials)
  }
}

This should result in you seeing the errors on your (error: any) => {...} function

Related