While adding Angular Universal to our app, we had to set the initialNavigation="enabled"
flag on the router, in order to avoid flickering.
Now this comes with 2 issues for us:
APP_INITIALIZER
s to load, routes that are guarded cannot be loaded, because the guard always assumes the user is unauthorized, because the check is happening in the APP_INITIALIZER
I found several Github issues about this (i.e. https://github.com/angular/universal/issues/1623) but none of them really provided a solution.
How can I use initialNavigation="enabled"
while at the same time wait for the APP_INITIALIZER
s to be executed?
edit (01/02/2021): in Angular 11 the wording has changed and the option is now called enabledBlocking
. The issue mentioned here however is not touched by this.
I will provide the solution I found here as well. I also posted it as an issue at Angular Universal's Github repository. If there are changes made to Universal, which will make this easier, I will update this answer.
Solution:
Basically what I do now, is to fetch the data about the pages, both in the server and the app, before the Angular app gets bootstrapped at all. Altering the routes
-array within app-routing.modules
-constructor apparently is early enough to get the dynamic routes in, before the router does its initial navigation.
It looks more or less like this (as stated by Nicolae this can be refactored to avoid the duplicate code):
server.ts
:
server.get('*', (req, res) => {
// fetch dynamic routes
// /!\ duplicate code to src/main.ts
fetch('http://static.content/')
.then(response => response.json())
.then(resp => {
const routes = resp.entries.map(route => ({
path: route.path,
component: StaticContentComponent,
data: {
id: route._id,
name: route.name
}
}));
res.render(indexHtml, {
req,
providers: [
{ provide: APP_BASE_HREF, useValue: req.baseUrl },
{ provide: DYNAMIC_ROUTES, useValue: routes }
]
});
});
});
return server;
}
and basically the same in main.ts
:
document.addEventListener('DOMContentLoaded', () => {
// fetch dynamic routes
// /!\ duplicate code to server.ts
fetch('http://static.content/')
.then(response => response.json())
.then(resp => {
const routes = resp.entries.map(route => ({
path: route.path,
component: StaticContentComponent,
data: {
id: route._id,
name: route.name
}
}));
platformBrowserDynamic([
{ provide: DYNAMIC_ROUTES, useValue: routes }
])
.bootstrapModule(AppModule)
.catch(err => console.error(err));
});
});
And then in my app-routing.module.ts
I add the data provided in DYNAMIC_ROUTES
to the routes:
const DYNAMIC_ROUTES = new InjectionToken<IEnvironment>('dynamicRoutes');
@NgModule({
imports: [
RouterModule.forRoot(routes, {
initialNavigation: 'enabled'
})
],
exports: [RouterModule]
})
export class AppRoutingModule {
constructor(@Inject(DYNAMIC_ROUTES) private dynamicRoutes, private router: Router) {
const config = router.config;
config.unshift(...this.dynamicRoutes);
this.router.resetConfig(config);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With