Wait for http inside Guard on Angular 5
Solution 1
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)
});
}
Solution 2
just import map
operator and it will work :
import { Observable } "rxjs/Observable";
import "rxjs/add/operator/map";
canActivate(): Observable<boolean>{
return this._api.apiGet('my/url').map(response => {
if ( response.status === 'success') {
// Consume data here
return true;
}
return false;
});
}
celsomtrindade
Amante dos códigos. Fascinado pela mágica digital. "Nunca subestime a capacidade de um usuário. Ele irá quebrar o mais simples dos códigos, com o mais simples uso de interface."
Updated on July 24, 2022Comments
-
celsomtrindade over 1 year
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)); }
-
Jota.Toledo over 6 yearswhy first() at the end?
-
celsomtrindade over 6 yearsYour code is just missing "from" on the Observable import, but it's working. Also, on the Angular 5 release blog post it says I should import the map like this:
import { map } from 'rxjs/operators';
but it's not working. It just work when I import the old way. Maybe this is what was wrong with my code (?) -
Jota.Toledo over 6 years@celsomtrindade read the article carefully and complete, there is a difference on how to use the elements from 'rxjs/operators'
-
charle819 almost 6 yearshi , can u please explain your code , like why using map in place of subscribe , using of() and other such things.
-
Jota.Toledo almost 6 yearsInternally, angular subscribes to the observable returned by the guard. For that reason, the only thing that the guard needs to do is to transform the observable returned by the api service into a boolean stream. In the case that the source observable, the HTTP request, fails,the guard still has to return a stream, not throw. Thats why I use
of
to map the error to a new stream. @charle819 -
charle819 almost 6 yearsoh ok , but there are various cases where if we try to call a method from inside the AuthGuard (which basically calls a service) , the AuthGuard does not seems to wait for the response of the service, can u tell why ?
-
Jota.Toledo almost 6 years"various cases" is too vague. Depends on what you are actually doing. Feel free to open a new question and tag me on it.