Angular, Module Federation, and remote internal routing 🤯🤯🤯
Julie Gladden
Posted on February 16, 2023
Hey folks,
There's tons of content out there for Angular with Webpack's Module Federation, specifically for SPAs. Because, if we are being totally honest with ourselves, that's how it was designed to be. 🧠🧠ðŸ§
However, we are just going to conveniently ignore that fact and have microfrontends that aren't quite so... micro. Besides, there's no one here to stop us, so let's do some things that maybe we shouldn't.
First things first, this article is going to assume you've done the hard work of setting up your Shell and your Remote (or Remotes).
Start with our routing in the shell, which lazy loads our remote. This should have been covered in any random tutorial on the internet.
shellRoutes = [
{
path: 'myRemote',
loadChildren: () =>
import('myRemoteApp/Module').then((m) => m.RouterModule)
}
]
//In our imports array...
RouterModule.forRoot(shellRoutes, { useHash: true})
Then, in our remote
remoteRoutes = [
{
path: '',
component: MyHomeComponent
},
{
path: 'list/:id',
component: MyRandomListComponent
}
]
/* Also, be aware that when you use RouterModule, for the
components in your remote to be rendered in the <router-outlet>
in your shell, you must use RouterModule.forChild() */
//In imports array
RouterModule.forChild(remoteRoutes)
Now, we would assume that we can just use routing as normal from our remote. However, trying
this.router.navigate(['list/1'])
in our remote will cause issues.
What happens is instead of just navigating directly to that component, it replaces everything that was already in the url after our hash.
For example, instead of getting:
localhost:3000/#/myRemote/list/1
We end up with:
localhost:3000/#/list/1
So now, our shell's router doesn't know to be looking inside of the remote project for its paths.
Your remote doesn't know what path was set by the shell, since they don't communicate with each other, so it just overwrites and starts fresh with it's own path. Even if you do
this.router.navigate(['myRemote/list/1'])
, it won't work.
Since the remote doesn't speak with the shell, the shell can't find that remote internal path. The shell doesn't even realize you are trying to change paths.
Here's the workaround. Have the shell do the all the routing.
To do this, we are going to utilize a custom event, and an event listener.
In our remote:
myNavigationFunction() {
const routeChangeEvent = new CustomEvent('childRouteChanged', {
data: {
remoteName: 'myRemote',
routeName: 'myRemote/list' + this.id
},
});
window.dispatchEvent(routeChangeEvent);
/* You may or may not need the data for remoteName, but it can be handy to have, so I pass it anyways. */
In our shell:
/* Listens for the remote to fire its event, and on firing, fires the function 'changeRoute()' */
@HostListener('window.childRouteChanged', ['&event'])
changeRoute(event) {
this.router.navigate([event.data.routeName])
}
And there you go, you should have a functioning shell and remote relationship that allows you to trigger routing from your remote.
Hope you enjoyed this article,
Julie
Posted on February 16, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.