DEV Community

Cover image for Angular, Module Federation, and remote internal routing 🤯🤯🤯
Julie Gladden
Julie Gladden

Posted on

Angular, Module Federation, and remote internal routing 🤯🤯🤯

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})

Enter fullscreen mode Exit fullscreen mode

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)

Enter fullscreen mode Exit fullscreen mode

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. */

Enter fullscreen mode Exit fullscreen mode

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])
}

Enter fullscreen mode Exit fullscreen mode

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

Top comments (1)

Collapse
 
avantijundare profile image
Avanti-Jundare

can we please get the github repo url for this. as in remote and shell app complete project