0

I want to load a specific Angular Module based on a user role on the same ("") path (home). Let's say, I have two modules named AdminModule and OperatorModule. If the role is ADMIN then I want to load the AdminModule otherwise the OperatorModule. I want to archive this with an Angular Guard.

Now in app.routing.ts I added the following code:

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

import { AuthGuard } from './core/guards/auth.guard';

import { AdminModule } from './modules/admin';
import { OperatorModule } from './modules/operator';

const routes: Routes = [
   {
      path: '',
      loadChildren: () => AdminModule,
      canLoad: [ AuthGuard ],
      data: { role: 'ADMIN' }
   },

   {
      path: '',
      loadChildren: () => OperatorModule,
      canLoad: [ AuthGuard ],
      data: { role: 'OPERATOR' }
   },
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

I have implemented an AngularGuard with the following code, which has to show the OperatorModule:

import { Injectable } from '@angular/core';
import { CanLoad, Route, Router } from '@angular/router';

@Injectable({
   providedIn: 'root'
})
export class AuthGuard implements CanLoad {
   constructor(private router: Router) {}

   canLoad(route: Route): Promise<boolean> | boolean {
      if (route.data.role === 'OPERATOR') {
         return true;
      }

      return false;
   }
}

Somehow it stops looking after the first route fails, am I doing something wrong?

StackBlitz example by djerid: https://stackblitz.com/edit/angular-vd4oyu?file=app%2Fapp-routing.module.ts

===

Matcher does not work either:

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

import { AuthGuard } from './core/guards/auth.guard';

import { AdminModule } from './modules/admin';
import { OperatorModule } from './modules/operator';

const routes: Routes = [
   {
      loadChildren: () => AdminModule,
      matcher: AdminMatcher,
      data: { role: 'NO' }
   },

   {
      loadChildren: () => OperatorModule,
      matcher: OperatorMatcher,
      data: { role: 'OPERATOR' }
   },
];

@NgModule({
   imports: [RouterModule.forRoot(routes)],
   exports: [RouterModule]
})
export class AppRoutingModule {
   constructor() {

   }
}


import { UrlSegment, UrlSegmentGroup, Route } from '@angular/router';
export function AdminMatcher(segments: UrlSegment[], group: UrlSegmentGroup, route: Route) {
   const isPathMatch = segments[0].path === route.path;

   if (isPathMatch && route.data.role === 'ADMIN') {
      return { consumed: [segments[0]] };
   } else {
      return null;
   }
}

export function OperatorMatcher(segments: UrlSegment[], group: UrlSegmentGroup, route: Route) {
   const isPathMatch = segments[0].path === route.path;

   if (isPathMatch && route.data.role === 'OPERATOR') {
      return { consumed: [segments[0]] };
   } else {
      return null;
   }
}
8
  • Can you reproduce the issue on stackblitz? Commented Apr 12, 2019 at 13:35
  • 1
    Here a similar issue: stackoverflow.com/questions/49405281/… Commented Apr 12, 2019 at 13:42
  • @MichaelDesigaud I see, but the solution they have is to have the modules on different paths. I think it is not possible, right? :( Commented Apr 12, 2019 at 13:46
  • @TilakDewangan added a Stackblitz with the exact same issue by djerid :) Commented Apr 12, 2019 at 13:47
  • @YanickvanBarneveld Yes if you have identical paths the last one will override the others Commented Apr 12, 2019 at 13:55

2 Answers 2

0

the best is to use UrlMatchers instead of canLoad for that, to match each one base on your conditions, when one of the 2 path is matched the other one will be ignored automatically

 const routes: Routes = [{
  path: '',
  matcher: adminMatcher,
  oadChildren: () => AdminModule,
  data: { role: 'ADMIN' }
 },
 {
  path: '',
  matcher: operatormatcher,
  loadChildren: () => OperatorModule,
  data: { role: 'OPERATOR' }

 }]

check this example

Sign up to request clarification or add additional context in comments.

1 Comment

I tried however does not seems to work, see edited answer. Also with removing isPathMatch is does not work... :(
0

This is an old question, but I am leaving an answer here in case someone stumbles upon this question. I solved this problem by using the root module's injector in my lazy-loaded route.

You can check out the working solution on stackblitz.

Solution

Step 1

Create a new file that exports an ReplaySubject<Injector>.

import { Injector } from '@angular/core';
import { ReplaySubject } from 'rxjs';

// Feel free to optimize this implementation if you want to
// expose `Observable<Injector>` instead of the subject itself.
export const appInjector = new ReplaySubject<Injector>();

Step 2

In main.ts, get a handle to the root module's Injector and publish it to the appInjector subject you created above:

platformBrowserDynamic()
  .bootstrapModule(AppModule)
  .then((m) => appInjector.next(m.injector)) // publish root module's injector
  .catch((err) => console.error(err));

Step 3

This is the step where we modify the route that needs to asynchronously lazy-load different modules based on some asynchronous condition.

const routes: Routes = [
  // other routes
  // ...
  // ...
  // conditional route
  {
    path: "dashboard",
    component: LayoutComponent,
    canActivate: [DashboardAuthGuard], // block this route if user is not logged-in
    loadChildren: () =>
    
      // Use the appInjector subject
      appInjector.pipe(
        
        // ...to get a handle to your AuthService
        map((injector) => injector.get(AuthService)),
        
        // ...then switch to a new observable
        switchMap((authService) => {
          
          // ...that uses authService to retrieve the logged-in user
          return authService.user$.pipe(
            
            // ...then switches again, this time to actually lazy-load a feature module
            switchMap((user) => {
              
              // ...but first let's check the user's role
              switch (user.role) {
                
                // ...and load Admin Feature Module if user.role is 'admin'
                case "admin":
                  return import(
                    "./modules/admin-dashboard/admin-dashboard.module"
                  ).then((m) => m.AdminDashboardModule);
                
                // ...or load User Feature Module if user.role is 'user'
                case "user":
                  return import(
                    "./modules/user-dashboard/user-dashboard.module"
                  ).then((m) => m.UserDashboardModule);
              }
            })
          );
        })
      ),
  },
];

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.