I'm developing an Angular 2 app with multiple ponents that rely on some data that is loaded from the server via the http-service (it's data about the user and his roles).
Most of my route ponents throw errors within their ngOnInit() methods if this data is not already loaded. The data is loaded and stored within a service that is injected in all ponents.
Is there a way to delay the rendering of the current route within my root-ponent until the http call is finished?
Otherwise I would have to implement some kind of check and retry mechanism within the ngOnInit of all the route ponents, which would be very awkward.
I already tried to hide the router-outlet element until the call finished but this leads to an error saying "Cannot find primary outlet to load xxx"
I'm developing an Angular 2 app with multiple ponents that rely on some data that is loaded from the server via the http-service (it's data about the user and his roles).
Most of my route ponents throw errors within their ngOnInit() methods if this data is not already loaded. The data is loaded and stored within a service that is injected in all ponents.
Is there a way to delay the rendering of the current route within my root-ponent until the http call is finished?
Otherwise I would have to implement some kind of check and retry mechanism within the ngOnInit of all the route ponents, which would be very awkward.
I already tried to hide the router-outlet element until the call finished but this leads to an error saying "Cannot find primary outlet to load xxx"
Share Improve this question asked Dec 8, 2016 at 10:13 SimonSimon 2964 silver badges17 bronze badges 2- The service is the same everwhere? If yes, you could use APP_INITIALIZER to handle this problem – LoïcR Commented Dec 8, 2016 at 10:18
-
1
You can use
Resolve
class and create aResolver
as a data provider for your ponents, it gathers data before your ponent is initialized. – ulubeyn Commented Dec 8, 2016 at 11:07
2 Answers
Reset to default 6There is an option to delay of router initial navigation when creating Router module:
RouterModule.forRoot([
// routes here
], {
initialNavigation: false // the propery to delay navigation
}),
Later the initial navigation can be triggered via Angular Router, like this:
this.router.initialNavigation();
I acplished this with a CanActivate guard. The key to making it work is returning an Observable from the canActivate method. That way, you can delay as long as you need to.
import { CanActivate } from '@angular/router';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Rx';
import { StateService } from '../../_services/state.service';
import { Subject } from 'rxjs/Rx';
import { Subscription } from 'rxjs/Subscription';
@Injectable()
export class LoadingGuard implements CanActivate {
constructor(private state: StateService) {}
public canActivate(): Observable<boolean> {
if (!this.state.loading$.getValue()) {
return Observable.of(true);
}
let subject = new Subject<boolean>();
let subscription = this.state.loading$.subscribe(value => {
if (!value) {
subject.next(true);
subject.plete();
subscription.unsubscribe();
}
});
return subject;
}
}
Above, StateService is a service that evaluates the current user and pre-caches some data for the rest of the app. It has a subject named loading$
that returns false when loading has pleted.
All that's left is to declare the guard in the app module.
import { LoadingGuard } from './app/loading-guard/loading-guard';
// other imports omitted
@NgModule({
// other module properties omitted
providers: [LoadingGuard]
})
export class AppModule {}
Then declare the guard in your routing.
import { LoadingGuard } from './app/loading-guard/loading-guard';
// other imports omitted
export const rootRouterConfig: Routes = [
{ path: 'app', ponent: AppComponent,
canActivate: [LoadingGuard],
children: [
{ path: 'index', ponent: IndexComponent },
// child routes omitted
] },
{ path: 'sign-in', ponent: SignInComponent },
{ path: '**', redirectTo: 'sign-in' }
];
For reference, here's the documentation on CanActivate: https://angular.io/docs/ts/latest/api/router/index/CanActivate-interface.html