I'd like to attach a component to a route asynchronously, given a condition.
The following example, which works (but is asynchronous), loads one component or another depending on the user role:
import { UserDashboardComponent } from './user-dashboard.component'
import { AdminDashboardComponent } from './admin-dashboard.component'
const role = 'admin' // Just for the example
const comp = role === 'admin' ? AdminDashboardComponent : UserDashboardComponent
const routes: Routes = [
{ path: '', component: comp },
]
But, let's say we want to retrieve the role from an API, thus asynchronous. What's the way to accomplish that?
You could create a generic.module.ts which will have both components in declarations array:
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { UserDashboardComponent } from './user-dashboard.component'
import { AdminDashboardComponent } from './admin-dashboard.component
@NgModule({
imports: [ BrowserModule ],
declarations: [ UserDashboardComponent,AdminDashboardComponent ]
})
export class GenericModule { }
this way you will have a module that contains the modules which you want to load.
Now next step will be to load them asynchronously using compiler:
inside your component do following:
import {GenericModule} from './generic.module';
import { Component, Input,ViewContainerRef, Compiler, NgModule,ModuleWithComponentFactories,OnInit,ViewChild} from '@angular/core';
@Component({
selector: 'generic',
template: '<div #target></div>'
})
export class App implements AfterViewInit {
@ViewChild('target', {read: ViewContainerRef}) target: ViewContainerRef;
constructor(private compiler: Compiler) {}
ngAfterViewInit() {
this.createComponent('<u>Example template...</u>');
}
private createComponent(template: string,role:string) {
@Component({template: template});
const mod = this.compiler.compileModuleAndAllComponentsSync(GenericModule);
const factory = mod.componentFactories.find((comp) =>
//you can add your comparison condition here to load the component
//for eg. comp.selector===role where role='admin'
);
const component = this.target.createComponent(factory);
}
}
Hope this helps.
Angular 2 supports lazy loading at module level. Feature modules are loaded asynchronously, not component.
You can make that component feature module.
https://angular.io/docs/ts/latest/guide/ngmodule.html
You could just define a higher-level component f.e. DashboardComponent which is included at route /dashboards
The parent component is then supposed to load the children in its template based on the asynchronous condition.
This way you will also need to use the *ngIf but at least its not cluttering the child components templates.
Since you will be using one route for either component, one solution is create another component for that route then its template could refer to both components but with an ngIf like below:
<user-dashboard *ngIf="role==='user'"></user-dashboard>
<admin-dashboard *ngIf="role==='admin'"></admin-dashboard>
I advice you make use of navigating programatically using the router's navigate method . So you list all the posible routes in the router file .Then in your component you call router.navigate() based on the specific case .
Very good solution could be "Use auth-guard". You can try implementing interfaces CanActivate/CanLoad and put your condition in that class.
Based on condition, routes gets activated.
Example:
import { Injectable } from '@angular/core';
import {
CanActivate,
ActivatedRouteSnapshot,
RouterStateSnapshot
} from '@angular/router';
import { Observable } from 'rxjs/Observable';
import { Router } from '@angular/router';
@Injectable()
export class AuthorizationGuard implements CanActivate {
constructor() {}
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot):
Observable<boolean> {
let url: string = state.url;
return this.canbeLoaded(url);
}
canbeLoaded(url: string): Observable<boolean> {
return Observable.of(true); //put your condition here
}
}