Search code examples
angularangular-routingangular-routerangular8

How to get current route's module in Angular


I'm working on a simple component that would change data based on the current route's module. I'm not sure where to find the information about that module.

Router

const routes: Routes = [
    {
        path: '', 
        loadChildren: () => import('@/app/homepage/homepage.module').then(m => m.HomepageModule)
    },
    {
        path: 'search', 
        loadChildren: () => import('@/app/search/search.module').then(m => m.SearchModule)
    }
];

AppComponent (root)

<div *ngIf="isHomepageModule; else otherModules">...</div>
<ng-template #otherModules>
    <div>...</div>
</ng-template>
<router-outlet></router-outlet>
constructor(private route: ActivatedRoute) {}

get isHomepageModule() {
    // This would be the ideal situation... 
    // Unfortunately, I cannot find any information about the module here
    return this.route.snapshot.module instanceof HomepageModule;
}

Is the module information even accessible? I would prefer doing the type checking or module name comparison than just "brute-forcing" the check with some regex matching of the current url. Thank you in advance.


Solution

  • I ended up changing the original idea a bit and compared the routes/modules based on data property of the routes. Also, I created the AppService which keeps track of the current route. This is useful as the route's tree traversing can be an expensive piece of logic, which would be better to not duplicate.

    // app.model.ts
    export interface OnAppInit {
      ngOnAppInit: () => void;
    }
    

    OnAppInit interface creates a kind of standard for how to use APP_INITIALIZER similarly to interfaces like OnInit, OnDestroy, etc.

    // app.service.ts
    @Injectable()
    export class AppService implements OnAppInit {
    
      route$: BehaviorSubject<ActivatedRouteSnapshot> = new BehaviorSubject<ActivatedRouteSnapshot>();
    
      constructor(private router: Router, private route: ActivatedRoute) {}
    
      ngOnAppInit() {
        this.route.events.pipe(
          filter(event => event instanceof NavigationEnd)
        ).subscribe(() => {
          let route = this.route.snapshot;
          while (route.firstChild) {
            route = route.firstChild;
          }
          this.route$.next(route);
        });
      }
    }
    
    // app.modules.ts
    @NgModule({
      // ...
      providers: [
        // ...
        {
          multi: true,
          provide: APP_INITIALIZER,
          useFactory: function initializeRoutes(appService: AppService) {
            return () => appService.ngOnAppInit();
          },
          deps: [AppService]
        }
      ]
    })
    export class AppModule {}
    

    The AppService listens to all NavigationEnd events happening on router and traverse the route tree to get the last child, which should be our current route. Our most recent route should now be the next value of route$ property which we can subscribe to.

    // app.component.ts
    @Component(/* ... */)
    export class AppComponent implements OnInit {
    
      constructor(private appService: AppService) {}
    
      ngOnInit(): void {
        this.appService.route$.subscribe(route => console.log(route.data));
      }
    }
    

    So the only thing missing is the new routes configuration with the data property set.

    const routes: Routes = [
      {
        path: '',
        data: { showHeader: true },
        loadChildren: () => import('@/app/homepage/homepage.module').then(m => m.HomepageModule)
      },
      {
        path: 'search',
        data: { showHeader: false },
        loadChildren: () => import('@/app/search/search.module').then(m => m.SearchModule)
      }
    ];
    

    and as we want to have access to the data in the current route (the last child of route tree), we need to configure the routes module to inherit the data.

    // app.module.ts
    @NgModule({
      // ...
      imports: [
        // ...
        RouterModule.forRoot(routes, {
          paramsInheritanceStrategy: 'always',
        })
      ]
    })
    export class AppModule {}