A. Vreeswijk
A. Vreeswijk

Reputation: 954

Angular typescript Async HTTPClient call

I have a problem. In my angular project I am trying to do a Http POST call, to login in my website. I provide the call with an username and password. If the API endpoint returns null, the login failed and if the endpoint returns an Account object it succeeded. The code to make the Http POST call is the following:

import { Injectable } from '@angular/core';
import {Account} from "../models/Account";
import {Observable} from "rxjs";
import {HttpClient, HttpHeaders} from "@angular/common/http";
import {PostService} from "./post.service";

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

    public loggedInAccount: Account | null;

    private httpOptions = {
        headers: new HttpHeaders({
            'Content-Type':  'application/json',
            //Authorization: 'my-auth-token'
        })
    };

    constructor(private httpClient: HttpClient) {
        this.loggedInAccount = null;
    }

    public login(account: Account):Account | null {
        this.restLogin(account).subscribe(
            (data) => {
                if (data != null) {
                    this.loggedInAccount = data;
                }
                return data;
            },
            (error) => {
                alert('Error: Status ' + error.status + ' - ' + error.error);
            });
        return null;
    }


    private restLogin(account: Account): Observable<Account> {
        let email = account.email;
        let password = account.password;
        return this.httpClient.post<Account>('http://localhost:8080/account/login', {email, password}, this.httpOptions);
    }

}

The problem is that the login() function always returns null. I understood I had to use some kind of async/await, but I can't seem to figure out how to use it in my case. Here is what I tried. In the AccountService:

public async login(account: Account):Promise<Account | null> {
    let returnValue = null;
    await this.restLogin(account).subscribe(
        (data) => {
            if (data != null) {
                this.loggedInAccount = data;
            }
            returnValue = data;
        },
        (error) => {
            alert('Error: Status ' + error.status + ' - ' + error.error);
        });
    return returnValue;
}

On the login page:

public onLoginClick(): void {
    let email = (document.getElementById("txtEmail") as HTMLInputElement).value;
    let password = (document.getElementById("txtPassword") as HTMLInputElement).value;

    if (this.accountService.login(new Account(email, password)) != null) {
        this.accountService.loggedInAccount!.posts = this.postService.getAccountPosts(this.accountService.loggedInAccount!.id);
        this.route.navigate(['/']);
    }
    else {
        (document.getElementById("txtMessage") as HTMLDivElement).innerHTML = "Incorrect email/password!"
    }
}

But this gives me an error on the login page on this line:

this.accountService.loggedInAccount!.posts = this.postService.getAccountPosts(this.accountService.loggedInAccount!.id);

saying that this.accountService.loggedInAccount is null, but that couldn't be possible, because I set it with this in de AccountService:

if (data != null) {
    this.loggedInAccount = data;
}
return data;

Can someone tell me how to fix and optimize (if possible) this?

Upvotes: 0

Views: 1695

Answers (1)

Junior Dussouillez
Junior Dussouillez

Reputation: 2397

The problem is that the login() function always returns null

Yes, there is a problem with how you return data from the login method. You use subscribe which is executed in async but does not return any value. You should use something like map. See this post with more details : Return a Observable from a Subscription with RxJS

So your login function should work if you try something like this (not tested) :

public login(account: Account): Observable<Account | null> {
    return this.restLogin(account)
        .pipe(
            map(result => {
                // TODO: do something with "result" ?
                // If not, you can remove the "map" pipe and only keep "catchError"
                return result;
            }),
            catchError(error => {
                // TODO: handle error
                return of(null);
            })
        );
}

Then you can call your login method like this :

this.accountService.login(new Account(email, password)).subscribe(account => {
    if (account !== null) {
        // Login succeeded, redirect
    } else {
       // Login failed
    }
})

Upvotes: 2

Related Questions