Angular, Module Federation, and remote internal routing 🤯🤯🤯
Tags: angular, webpack, tutorial, microservices
Published: 2023-02-16 02:20:49

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. 

```javascript

shellRoutes = [
  {
    path: 'myRemote',
    loadChildren: () => 
        import('myRemoteApp/Module').then((m) => m.RouterModule)
  }
]

//In our imports array...
RouterModule.forRoot(shellRoutes, { useHash: true})

```

Then, in our remote

```javascript

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**:

```javascript

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**:

```javascript

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