DOM-less Routing Engine Allowing Madness.
This work was derived from a need for large enterprise internal facing applications to co-exist in a desktop like environment in the browser. The Angular Router was designed specifically for tying what is shown on the page to the location in the Browser. However, in some instances this can be behavior that is not desired. A good example of this is a dashboard with complex and rich widgets (charting, single metric, session tracing tools, etc). The default routing solution works really well until a widget such as a graph (with multiple routes) needs to be rendered multiple times on the page for different metrics.
Run yarn run-all
to get all MFEs running,
Navigate to all below locally to see each application is able to open separately.
http://localhost:4001 - Counter
http://localhost:4002 - Scoreboard
http://localhost:4003 - Tour of Heroes
After verifying, navigate to http://localhost:4004 to play around with the Dashboard
Due to the power of Angular's Dependency Injection, we can create an isolated injector per widget. We can accomplish this by creating a custom service that provides code-splitting for each widget. This is accomplished by using the widget host component as the parent injector for each widget.
When we create our sandbox, we will also need to provide our own "override" for Router
and Store
and PlatformLocation
. Overriding the PlatformLocation at the widget level is the key to allowing multiple routers to exist. The parent (root app router) will still have it's default behavior of deep linking, but the widgets will use an "in memory" url.
Since each application has a sandboxed instance of the Router and Ngrx, we can have the application render a new instance of itself in itself.
Let's say we wanted to release the counter as a single page app but also release it in our MFE Dashboard.
By taking what would be considered the AppModule
and migrating it into a "library" will allow for the build process to consume the core functionality for two separate distribution styles.
- The AppModule is now responsible properly bootstrapping the application for SPA distribution. It's sole responsibility is NgModule that imports the CounterModule and has its own bootstrapped AppComponent that invokes the CounterContainerComponent.
- The MfeAppModule is responsible for creating a NgModule that implements the LoadableApp interface that provides the EntryComponent to be rendered in as a Federated Module.
The
MfeAppModule
EntryComponent references the same CounterContainerComponent that was used for the SPA distribution, allowing for multiple distribution types of the same application.
- Hovering over a
<a [routerLink]="...">...</a>
will present a URL in the browser that will not work.- Should not be used with Angular Universal or any public facing site due to this.
- Can become more supported by working with Angular team.
- Functionality such as
window.history.back()
andwindow.history.forward()
do not work since we essentially ripped out all communication with the browser. - To be successful with isolation with providers that hold state, refrain from using
@Injectable({ providedIn: '...' }}
syntax to avoid the compiler hoisting to an injector out of the sandbox.