Search code examples
angularauthenticationservice

ERROR TypeError: Cannot read properties of undefined (reading 'message') in Angular Login


In my Angular 13 I have this code:

JSON Response from ASP.NET Core Web API:

{
   "code": 200,
   "error": false,
   "message": "Logged In Successful",
   "results": {
       "token": "thisismytoken...",
       "user": {
           "id": 1,
           "username": "Ashwel",
          },
       "roles": [
           "SuperAdmin"
       ]
   }
}

ANGULAR

user.ts:

export interface IResponse<T> {
  message: string;
  error: boolean;
  code: number;
  results: T;
}

export interface IUser 
{
  username?: string;
  token?: string;
  roles?: string[];
}

auth.service:

export class AuthService {
  baseUrl = environment.apiUrl;
  private currentUserSource = new ReplaySubject<IUser | null>(1);
  currentUser$ = this.currentUserSource.asObservable();

  constructor(private http: HttpClient, private router: Router) { }
  login(model: any){
    return this.http.post(this.baseUrl+'auth/login', model).pipe(
      map((res:IUser)=>{
        const user = res;
        if(user){
          this.setCurrentUser(user);
        }
      })
    )
  }

  setCurrentUser(user: IUser){
    if(user && user.token){
      user.roles = [];
      const roles = this.getDecodedToken(user.token).role;
      Array.isArray(roles) ? user.roles = roles : user.roles.push(roles);
      localStorage.setItem('user', JSON.stringify(user));
      this.currentUserSource.next(user);
    }
  }

  getDecodedToken(token: string) {
    return JSON.parse(atob(token.split('.')[1]));
  }
}

auth.component:

export class AuthComponent implements OnInit {
  loginForm!: FormGroup;
  user!: IUser | null;

  constructor(
   private authService: AuthService,
   private router: Router,
   private toastr: ToastrService
   ) {
   this.authService.currentUser$.pipe(take(1)).subscribe(user=> this.user = user);
 }

myForm() {
  this.loginForm = new FormGroup({
    UserName: new FormControl('', Validators.required),
    Password: new FormControl('', [Validators.required])
  })
}

login(){
  this.authService.login(this.loginForm.value).subscribe({
    next: (res: any) => {
      console.log(res);
      this.toastr.success(res.message);
      this.router.navigateByUrl('dashboard');
    },
    error: (error) => {
      this.toastr.error(error.message);
    }
  })
 }
}

This is the flow auth.component -> auth.service -> user.ts:

What I intend to do is, when user submits to login:

  1. token, username and roles are stored in the localStorage
  2. when login is successful, it displays the message: Logged In successful
  3. it redirects to dashboard

But instead of all these, I got this error in the inspect -> console:

ERROR TypeError: Cannot read properties of undefined (reading 'message')

When I did console.log(res) in auth.service, I got undefined.

How do I resolve this error?

Thank you


Solution

  • I see two mistakes in your AuthService.login method:

    • You have the response label as IUser when based on the JSON you've posted it should be of IResponse<IUser>>
    • You are using the map operator to trigger a side effect. map is used to apply a transformation to the emitted values and therefore the map function should return the transform value. To trigger side effects you should use tap.

    So you should modify the method as follows.

    login(model: any){
      return this.http.post<IResponse<IUser>>(this.baseUrl+'auth/login', model).pipe(
        tap((response) => {
          const user = response.results;
          if(user){
            this.setCurrentUser(user);
          }
        })
      )
    }
    

    cheers