How to properly structure angular app with multiple modules that can access one another?

1.1k Views Asked by At

I am still getting used to Angular and how to properly structure different components and modules. I want to set my app up according to best practices and maximize efficiency. I currently have multiple modules. I have a "base" module that I want to navigate to, but depending on the URL, I want to incorporate components from other modules. Here is how my app-routing.module is currently set up:

const routes: Routes = [
  { path: '', component: BaseComponent },
  { path: 'featureone', loadChildren: () => import('./feature-one/feature-one.module').then(m => m.FeatureOneModule) },
  { path: 'featuretwo', loadChildren: () => import('./feature-two/feature-two.module').then(m => m.FeatureTwoModule) },
  { path: '**', redirectTo: '', pathMatch: 'full'}
];

I understand this routing is not set up properly, but I am not sure how I can properly set this up in the most efficient way.

Currently, if I navigate to '', it will load the BaseComponent as expected. If I add <app-feature-one></app-feature-one> or <app-feature-two></app-feature-two> to the BaseComponent template, it will throw an error like "Cannot access 'FeatureOneModule' before initialization"

Is there some way where I could keep routes such as 'featureone' and 'featuretwo' where it would navigate to the BaseComponent, and I could add logic to display <app-feature-one></app-feature-one> or <app-feature-two></app-feature-two> and only load FeatureOneModule if I navigate to 'featureone' or FeatureTwoModule if I navigate to 'featuretwo'?

2

There are 2 best solutions below

0
On BEST ANSWER

With your current configuration, because { path: '', component: BaseComponent }, is first, no matter what url you issue, it will always resolve to BaseComponent. Angular resolves route by performing a DFS search and will stop at the first match, so you'd have to be concise when defining the routes.

A way to solve this would be do add pathMatch: 'full':

{ path: '', component: BaseComponent, pathMatch: 'full' },
...

it will throw an error like "Cannot access 'FeatureOneModule' before initialization"

You are getting this error because app-feature-one and app-feature-two are components that belong to lazy-loaded modules, so, unless you imperatively import those modules, you won't be able to use them.

Is there some way where I could keep routes such as 'featureone' and 'featuretwo' ...

A quick way to solve this would be to use named outlets:

const routes: Routes = [
  { 
    path: '', component: BaseComponent, pathMatch: 'full',
    children: [
       { path: 'featureone', loadChildren: () => import('./feature-one/feature-one.module').then(m => m.FeatureOneModule), outlet: 'feat-one' },
  { path: 'featuretwo', loadChildren: () => import('./feature-two/feature-two.module').then(m => m.FeatureTwoModule), outlet: 'feat2' },
   ]

  },
  { path: '**', redirectTo: '', pathMatch: 'full'}
];

then, in your base-component.component.html

<router-outlet name="feat-one"></router-outlet>

<!-- ... -->

<router-outlet name="feat-two"></router-outlet>

In order to navigate to one of them(or to both at the same time), you'll have to use something like this:

[routerLink]="[{ outlets: { 'feat-one': 'featureone' } ... }]"
0
On

As you want your BaseComponent to show up for every route, you should include it into your AppComponent component :

<app-base></app-base>

If the feature component needs to be displayed as a sibling of base component, just place the router outlet accordingly:

<app-base></app-base>
<router-outlet></router-outlet>

If the feature component should be nested into some part of your baseComponent, you can use content projection :

<app-base>
  <router-outlet></router-outlet>
</app-base>

Then in your BaseComponent, use ng-content :

<header></header>
  <ng-content></ng-content>
<footer></footer>