4
votes

I have an Angular 6 app with routing and lazy loaded modules. The AppModule has a route configuration containing two lazy loaded routes, let's say AModule and BModule.

We have different angular-cli environments configured for prod and dev.

During development of BModule I want the BModule to be available as a route on our development server, but not on our production server.

Therefore, we build the development version of the app using angular-cli environment dev, while the production version is build using environment prod.

So now and in the future it will be usual to have a route configuration for prod and a route configuration for dev which is a superset of the prod configuration.

So what I have done is I created two route configs:

export const prodRoutes: Routes = [
  { path: 'a-module', loadChildren: `app/a-module/a.module#AModule` },
];

export const devRoutes: Routes = [
  { path: 'b-module', loadChildren: `app/b-module/b.module#BModule` },
];

For prod I simply use variable prodRoutes for my route configuration. That works fine.

For dev configuration I set routes to [...devRoutes, ...prodRoutes]. That does not work properly. It seems that Angular does not understand merged route configurations.

Is there a way to merge multiple route arrays to a single working route configuration?

2
I tried it what you have tried in my project, and it seems working. Also, do you have AModule insinde b.module? Because, your dev route config tells me so. Is it a typo or on purpose? - Bunyamin Coskuner
I guess OP renamed his modules for the sake of example. It must be a typo - Guerric P
It was a typo. Sorry. I fixed it. - Pascal Chorus

2 Answers

4
votes

I've found that lazy loading with a "Development Guard" is working best.

development-guard.service.ts

@Injectable({
  providedIn: "root",
})
export class DevelopmentGuard implements CanActivate {
  canActivate() {
    return !environment.production;
  }
}

app-routing.module.ts

const routes = [
  { 
    path: "dev-page",
    canActivate: [DevelopmentGuard], 
    loadChildren: "app/development-only/development-only.module#DevelopmentOnlyModule" 
  },
  {
    path: "", 
    loadChildren: "app/production/production.module#ProductionModule" 
  },
]

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

app.module.ts

const developmentModules = []
if (!environment.production) {
  developmentModules.push(DevelopmentOnlyModule)
}

@NgModule({
  imports: [
    AppRoutingModule,
    ...developmentModules,
  ],
})

From what I can tell, this did not effect the size of my main.js file. I imagine there is a minimal increase because of the guard. But production will never load the "DevelopmentOnlyModule" and so I think it's okay.

Note: Make sure to import the default environment file.

1
votes

We had a similar issue and we handled it using the routes as an "environment-like" constants.

Here it is our app.routing.ts

import {appRoutes} from "../environments/general/approutes"; <----THAT'S IT

@NgModule({
    imports: [
        CommonModule,
        BrowserModule,
        RouterModule.forRoot(appRoutes, {useHash: true})
    ],
    exports: [
    ],
})

export class AppRoutingModule { }

The general folder under environemnt store the environment constants and file that will be used for dev mode.

So we have our approutes.ts file under /environments/general/approutes

..Omissis imports...
export const appRoutes: Routes =[
    {
        path: 'login', component: LoginComponent
    },
    { path: '', component: FullPageLayoutComponent,
        children: [
            { path: 'logged', loadChildren: '../../app/logged.module#LoggedModule' }
        ]
    },
    { path: '', component: ComponentA,
        children: [
            { path: 'pathA', loadChildren: '../../app/componentA.module#ComponentAModule' }
        ]
    },
    {
        path: '**', component: NotFoundComponent
    }
];

We then have a different approutes.ts file under /environments/prod/approutes

..Omissis imports...
export const appRoutes: Routes =[
    {
        path: 'login', component: LoginComponent
    },
    { path: '', component: FullPageLayoutComponent,
        children: [
            { path: 'logged', loadChildren: '../../app/logged.module#LoggedModule' }
        ]
    },
    { path: '', component: ComponentA,
        children: [
            { path: 'pathB', loadChildren: '../../app/componentB.module#ComponentBModule' }
        ]
    },
    {
        path: '**', component: NotFoundComponent
    }
];

Than obviously you have to configure yor angular.json with filereplacements for the prod environments with something like this:

        "prod":{
          "fileReplacements": [
            {
              "replace": "src/environments/general/environment.ts",
              "with": "src/environments/prod/environment.ts"
            }, //For sure you will have different stuff in your environment const in dev and prod mode
            {
              "replace": "src/environments/general/approutes.ts",
              "with": "src/environments/prod/approutes.ts"
            }
          ]
        }

What will happen?

That when launching with prod mode the filereplacement will happen first, and then your app.routing.ts will import the approutes constant to produce the routing and loading magic, including the lazily-module stuff.

By doing so you may have different routes and loaded module in different environment. This approch could be used to build different applications sharing the same base components, handling them as separe environments, all in one project and i found it very helpful

Hope it helped