Reputation: 51
In an Angular app, I would like to determine what module to load for a specified path. It's not about lazy loading. It's about dynamic route configuration/determination.
I have an app which have 3 kinds of access. Each access is in his own module. On app load, I would like to determine which module to load. At the root route level and the child level.
It seems to me that Angular is very flexible for many things but the routing configuration is very static...
Example:
// routed module
const routes: Routes = [
{ path: '', loadChildren: () => getModuleType() }
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
class ParentModule {}
// My problem is there, where can I put this function?
getModuleType() {
// How can I have the dependency injected?
return someApiService.getProfile().map(profile => {
if (profile === 'A') {
return ProfileAModule;
}
if (profile === 'B') {
return ProfileBModule;
}
});
}
// some feature module
@NgModule({})
class ProfileAModule {}
@NgModule({})
class ProfileBModule {}
Upvotes: 5
Views: 2010
Reputation: 151
I would use guarded features module to achieve this.
Example of folder structure for Feature X folder and Core folder containing the guard (You can have N features if you want) :
/core
/guard
/ feature-x.guard.ts
/feature-x
/feature-x.router.ts
/feature-x.module.ts
/feature-x.component.ts
/feature-x-home
/feature-x-home.module.ts
/feature-x-home.router.ts
/feature-x-home.component.ts
/feature-x-home.component.html
/feature-x-home.component.scss
/feature-x-route1
/feature-x-route1.module.ts
/feature-x-route1.router.ts
/feature-x-route1.component.ts
/feature-x-route1.component.html
/feature-x-route1.component.scss
Each feature contains its restricted routes. feature-x.router.ts
would be like this :
const featureXRoutes: Route[] = [
{
path: '',
component: RouteXFeatureComponent,
canActivate: [ FeatureXGuard ],
children: [
{ path: '', redirectTo: '/feature-x-home', pathMatch: 'full' },
{ path: 'feature-x-home', loadChildren: '@app/feature-x/feature-x-home/feature-x-home.module#FeatureXHomeModule' },
{ path: 'feature-x-route-1', loadChildren: '@app/feature-x/feature-x-route-1/feature-x-route-1.module#FeatureXRoute1Module' },
// ... etc
]
}
];
export const FeatureXRoutingModule: ModuleWithProviders = RouterModule.forChild(featureXRoutes);
@app is an alias for your source path folder. If you don't have one, use relative path instead. Lazy-loading is optional but i strongly recommend using it.
feature-x-home.router.ts
:
const featureXHomeRoutes: Route[] = [{
path: '',
component: FeatureXHomeComponent
}];
export const featureXHomeRoutes: ModuleWithProviders = RouterModule.forChild(featureXHomeRoutes);
feature-x.module.ts
:
@NgModule({
imports: [
FeatureXRoutingModule
],
declarations: [
FeatureXComponent
]
})
export class FeatureXModule {}
feature-x.component.ts
:
@Component({
selector: 'app-feature-x',
template: '<router-outlet></router-outlet>',
styleUrls: [ './feature-x.component.scss' ]
})
export class FeatureXComponent {}
You can provide any layout relative to the current feature.
app.module.ts
:
@NgModule({
imports: [
CoreModule, // contains all guards declaration
Feature1Module, // feature-x with x = 1
Feature2Module,
Feature3Module,
//... etc
FeatureNModule,
AppRoutingModule
],
declarations: [
AppComponent
],
bootstrap: [ AppComponent ]
})
export class AppModule {}
If you don't have a CoreModule then declare all the guards here with providers
property.
app.router.ts
:
const appRoutes: Route[] = [
{ path: '', redirectTo: '/defaultRoute', pathMatch: 'full' },
{ path: '**', redirectTo: '/defaultRoute' }
];
export const AppRoutingModule: ModuleWithProviders = RouterModule.forRoot(appRoutes);
feature-x.module.ts
:
const FEATURE_X = 'YOUR_FEATURE_X';
@Injectable()
export class FeatureXGuard implements CanActivate {
constructor(
private profilService: ProfilService,
private router: Router
) {}
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
const canGo = this.profileService.hasProfil(FEATURE_X);
if (!canGo) {
this._router.navigate([ this.profileService.getDefaultRouteFor(FEATURE_X) ]);
}
return canGo;
}
}
With this system, depending on his profile, an user will be redirected on the correct "home" page. If he tries to access a page for which he doesn't have the right profile, he will be redirected to his "home" page
You need to use APP_BOOTSTRAP_LISTENER hook for registering app routes depending on your user profile.
https://stackblitz.com/edit/angular-vjfne6
Upvotes: 1