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));
}
CanActivateChild - Decides if children routes of a route can be activated. CanDeactivate - Decides if a route can be deactivated.
AuthGuard is used to protect the routes from unauthorized access 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.
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.
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)
});
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With