Lucas Arruda
Lucas Arruda

Reputation: 553

How should I wait for http request to complete in Angular?

I've been trying to learn Angular (Typescript) + ASP Net Core and I dont understand how should I wait for an observable to complete. Basically, i've created a login service on the backend, which works. However, i've tried to create a simple helper to handle http responses easier on the client side. The issue is that it runs two times: on the first one it thinks RequestResponse is undefined (because it skips the http request) while on the second the RequestResponse is populated and it works as expected.

Here's the Http helper:

 get<T>(url: string): Observable<T> {
    let options = this.doHeaders();
    return this.doSub(this.http.get(this.doUrl(url), options));
}

private doSub<T>(obs: Observable<Response>) {
    var ob1: Observable<T> = obs.map(this.extractData)
        .catch(this.handleError);

    return ob1;
}

Here's the RequestResponse:

export class RequestResponse {
   public error: string;
   public isOk: boolean;
   public code : StatusCode; 
}

Here's the Service:

@(Injectable)
export class RequestService {

constructor(
    private net: NetworkService,
    private loaderService: LoaderService,
    private storage: StorageService,
) {
}

login(email: string, password: string, rememberMe: boolean = false): RequestResponse {
    var r = new RequestResponse();

     // I need to make this wait
     this.net.get<LoginModel>(`Authentication/Login/${email}/${password}`).subscribe(t => {
            this.storage.token = t.token;
            this.storage.email = rememberMe ? email : null;
            this.storage.password = rememberMe ? password : null;
            this.storage.user = t.user;
            r.isOk = true;
            r.code = StatusCode.OK;
        },
        error => {
            r.isOk = false;
            switch (error.message) {
            case '403':
                    r.code = StatusCode.Forbidden; r.error = 'You've been suspended'; return;
            case '404':
                    r.code = StatusCode.NotFound; r.error = 'User not found'; return;
            case '406':
                    r.code = StatusCode.NotAcceptable; r.error = 'Invalid Email/Password'; return;
            default:
                r.code = StatusCode.InternalServerError; r.error = 'Internal Error'; return;
            }
        });

    // First time it is skipping all the way down, returning null
    return r;
   }
}

Here's how i'm trying to use it:

login() {

    this.loginResponse = this.requestService.login(this.email, this.password, this.rememberMe);
    console.log(this.loginResponse);
    if (!this.loginResponse.isOk) {
        this.toasterService.pop('error', 'Error', this.loginResponse.error);
        return;
    }
    this.router.navigateByUrl('/home');
}

I've read about Promises and Observables and I've tried many changes, but the first RequestResponse being null was still a problem.

How should I deal with this?

Upvotes: 1

Views: 3105

Answers (1)

DeborahK
DeborahK

Reputation: 60518

It is recommended that you use the subscribe in the component, not in the service. That way you will have a better mechanism to react when the subscription is complete, plus provide ways to cancel or unsubscribe.

Code from my service

getProducts(): Observable<IProduct[]> {
    return this.http.get<IProduct[]>(this.productsUrl)
                    .pipe(
                        tap(data => console.log(JSON.stringify(data))),
                        catchError(this.handleError)
                    );
}

Code from my component

ngOnInit(): void {
    this.productService.getProducts().subscribe(
        (products: IProduct[]) => {
            this.products = products;
            // Any code to be run after the subscription is complete goes here
        },
        (error: any) => this.errorMessage = <any>error
    );
}

For a "complete" sample application, check out my github repo: https://github.com/DeborahK/Angular-GettingStarted

Upvotes: 3

Related Questions