Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Wait for http inside Guard on Angular 5

I'm using a Guard on an Angular application to resolve initial critical data. On the version 4 of Angular I was duing it like this:

// app.routing.ts
routing = [{
    path: '', component: AppComponent, canActivate: [ResolveGuard],
}];

// resolve.guard.ts
@Injectable()
export class ResolveGuard implements CanActivate {
    constructor(
        private _api: ApiService,
    ) { }

    canActivate(): any {
        return this._api.apiGet('my/url').map(response) => {
            if ( response.status === 'success') {
                // Consume data here
                return true;
            }

            return false;
        }).first();
    }
}

Since the new version of Http on Angular 5 doesn't use the .map() property anymore, this is not working.

If I change .map() to .subscribe() it doesn't throw any errors, but the application never resolve properly. On the other hand, using .first() and/or .map() throw some errors, as expected in this version.

What should I do in this case?

I need to activate that route only if and when the initial data is loaded.


Edit to add info about the apiGet function:

constructor(private _http: HttpClient) {}

public apiGet(url: string): any {
    return this._http
        .get(this.apiUrl + url)
        .catch(this.handleError.bind(this));
}
like image 702
celsomtrindade Avatar asked Nov 09 '17 20:11

celsomtrindade


People also ask

What is the difference between CanActivate and CanDeactivate in Angular?

CanActivateChild - Decides if children routes of a route can be activated. CanDeactivate - Decides if a route can be deactivated.

Why do we use AuthGuard in Angular?

AuthGuard is used to protect the routes from unauthorized access in angular.

What is the use of CanActivate in Angular?

CanActivatelink Interface that a class can implement to be a guard deciding if a route can be activated. If all guards return true , navigation continues. If any guard returns false , navigation is cancelled.

What is RouterState in Angular?

Angular RouterState is the state of the router as a tree of activated routes. It tells how the various components of an application are arranged on the screen to define what should be displayed on it. RouterState represents the state of the router as it keeps changing over time when users navigate from page to page.


1 Answers

So, first thing first: avoid the use of any when possible, specially when you know which type belongs where.

export interface FooInterface {
  status: string;
  fooString : string;
  fooNumber : number;
}

First Ill define the interface of the service in a clean manner, and then Ill refactor the guard class.

UPDATED ANSWER for rxjs 6.x

import { throwError, Observable } from 'rxjs';
import { catchError } from 'rxjs/operators';

@Injectable()
export class FooService {
   constructor(private _http: HttpClient) {}

   public apiGet(url: string): Observable<FooInterface> {
    return this._http
        .get<FooInterface>(this.apiUrl + url)
        .pipe(
          catchError(error => {
             // do general error handling if necessary and throw
            throwError(error);
           })
        );
  }
}

The guard class:

import { of, Observable } from 'rxjs';
import { map, catchError } from 'rxjs/operators';

@Injectable()
export class ResolveGuard implements CanActivate {
constructor(
    private fooService: FooService ,
) { }

canActivate(): Observable<boolean> {
    return this.fooService.apiGet('my/url')
       .pipe(
         map(response => response.status === 'success'),
         catchError(error => of(false))
       );
}

ORIGINAL ANSWER for rxjs 5.x

import { _throw } from 'rxjs/observable/throw':

constructor(private _http: HttpClient) {}

public apiGet(url: string): Observable<FooInterface> {
    return this._http
        .get<FooInterface>(this.apiUrl + url)
        .catch(error => {
          // do general error handling if necessary and throw
          _throw(error);
       });
}

The guard class:

import { of } from 'rxjs/observable/of';

@Injectable()
export class ResolveGuard implements CanActivate {
constructor(
    private _api: ApiService,
) { }

canActivate(): Observable<boolean> {
    return this._api.apiGet('my/url')
       .map(response => {
          let val = false;
          if ( response.status === 'success') {
                // Consume data here
                val = true;
          }
          return val;
        }).catch(error => {
          // consume the error maybe?
          of(false)
        });
}
like image 60
Jota.Toledo Avatar answered Sep 20 '22 16:09

Jota.Toledo