Skip to content

Commit 2e77e09

Browse files
committed
Cleanup README
1 parent f0cea66 commit 2e77e09

File tree

1 file changed

+5
-381
lines changed

1 file changed

+5
-381
lines changed

README.md

Lines changed: 5 additions & 381 deletions
Original file line numberDiff line numberDiff line change
@@ -1,386 +1,10 @@
1-
# Angular Elements Router
2-
3-
![License](https://img.shields.io/github/license/fboeller/ngx-elements-router) ![Build](https://img.shields.io/github/workflow/status/fboeller/ngx-elements-router/CI) ![Version](https://img.shields.io/npm/v/ngx-elements-router)
4-
5-
The angular elements router is a library for using the Angular Router within Angular Elements.
6-
7-
- **Router module usage** — Use the angular router module both in the platform and the micro frontend without interfering each other.
8-
9-
- **Dev platform** — Includes a straight-forward dev platform capable of replacing the production platform for local development.
10-
11-
- **Battle-tested** — This library is based on the efforts at [LeanIX](https://www.leanix.net/en/) where this approach is used to serve micro frontends to 100K users.
12-
13-
- **Non-intrusive** — Use only the features you need, easy opt-out once Angular starts supporting the router in Angular Elements out of the box.
14-
15-
- **No dependencies** — Besides Angular this library does not include any dependencies.
16-
17-
## Installation
1+
# Microfrontends with Angular
182

193
```
20-
$ npm install --save ngx-elements-router
21-
```
22-
23-
## Try it
24-
25-
This repo includes an example platform and an example micro frontend.
26-
27-
```
28-
$ git clone https://github.com/fboeller/ngx-elements-router.git
29-
$ cd ngx-elements-router
4+
$ git clone https://github.com/fboeller/microfrontends-with-angular.git
5+
$ cd microfrontends-with-angular
306
$ npm install
7+
$ npm start
318
```
329

33-
### Dev platform with micro frontend
34-
35-
```
36-
$ npm start bookings
37-
```
38-
39-
A visit to `localhost:4200` shows the dev platform.
40-
If you click the buttons, you see how the route changes, independent from if the click originated in the platform or the micro frontend.
41-
42-
### Angular platform with micro frontend
43-
44-
```
45-
$ npm run build bookings
46-
$ npm install -g http-server
47-
$ http-server dist/bookings --port 4201
48-
```
49-
50-
```
51-
$ npm start train-platform
52-
```
53-
54-
A visit to `localhost:4200` shows the Angular platform.
55-
The bundle file `main.js` is served from `localhost:4201` without hot-reloading.
56-
57-
## Prerequisites
58-
59-
You have an Angular application that acts as a [platform](./projects/train-platform) and an Angular application that acts as a [micro frontend](./projects/bookings).
60-
A build of the micro frontend results in a single build that registers custom elements on loading.
61-
62-
## Usage
63-
64-
The general idea of this library is the delegation of the modification of the browser url such that only the platform application modifies it.
65-
This is achieved by preventing Angular in the micro frontend application from accessing the browser url and instead using inputs and outputs
66-
of the web component to pass route changes from and to the router module of the platform.
67-
The micro frontend routes and the platform routes are both defined starting after the base href but the micro frontend routes typically define
68-
a single top-level route like `/micro-frontend` and only receive routes starting with that route.
69-
In the component mounted at `/micro-frontend`, it is then possible to use an absolute path `/abc` to refer to a route external to the micro frontend and
70-
a relative path `./abc` to refer to a route relative to the route `/micro-frontend`.
71-
72-
### Create a host component
73-
74-
To be able to reference your custom element in the routes, you need to create a host component.
75-
You can use the `aerRouting` on the custom element to pass route changes to the micro frontend and to allow the micro frontend to pass route changes to the platform.
76-
77-
[platform/micro-frontend-host.component.ts](./projects/train-platform/src/app/micro-frontend-host/micro-frontend-host.component.ts)
78-
79-
```typescript
80-
import { Component } from "@angular/core";
81-
82-
@Component({
83-
selector: "app-host",
84-
template: `<mf-entry aerRouting></mf-entry>`,
85-
})
86-
export class MicroFrontendHostComponent {}
87-
```
88-
89-
### Create a host module
90-
91-
To lazy load your custom element, you need to create a host module in the platform.
92-
Import `AngularElementsRouterModule` to be able to use the `aerRouting` directive.
93-
Use the schema `CUSTOM_ELEMENTS_SCHEMA` to make Angular accept the custom element in the host component.
94-
Use the path `**` to pass all sub paths to the custom element.
95-
96-
[platform/micro-frontend-host.module.ts](./projects/train-platform/src/app/micro-frontend-host/micro-frontend-host.module.ts)
97-
98-
```typescript
99-
import { AngularElementsRouterModule } from "ngx-elements-router";
100-
import { MicroFrontendHostComponent } from "./micro-frontend-host.component";
101-
102-
const routes: Routes = [
103-
{
104-
path: "**",
105-
component: MicroFrontendHostComponent,
106-
},
107-
];
108-
109-
@NgModule({
110-
declarations: [MicroFrontendHostComponent],
111-
imports: [RouterModule.forChild(routes), AngularElementsRouterModule],
112-
schemas: [CUSTOM_ELEMENTS_SCHEMA],
113-
})
114-
export class MicroFrontendHostModule {}
115-
```
116-
117-
### Bind the micro frontend to a route
118-
119-
Choose a route under which your micro frontend should be loaded.
120-
Use the `LoadBundleGuard` to load the bundle of your micro frontend on the first activation of the route.
121-
122-
[platform/app-routing.module.ts](./projects/train-platform/src/app/app-routing.module.ts)
123-
124-
```typescript
125-
import { LoadBundleGuard } from "ngx-elements-router";
126-
127-
const routes: Routes = [
128-
{
129-
path: "micro-frontend",
130-
canActivate: [LoadBundleGuard],
131-
data: {
132-
bundleUrl: "http://localhost:4201/main.js",
133-
},
134-
loadChildren: () =>
135-
import("./micro-frontend-host/micro-frontend-host.module").then(
136-
(m) => m.MicroFrontendHostModule
137-
),
138-
},
139-
];
140-
```
141-
142-
---
143-
144-
**NOTE**
145-
146-
Since Angular 11, your bundle name might be `main-es2015.js`.
147-
148-
---
149-
150-
### Register routing in the micro frontend
151-
152-
Use the `EntryRoutingService` in the Angular component representing the custom element.
153-
This way, route changes are passed to the Angular router in the micro frontend and in the other direction to the platform.
154-
155-
[micro-frontend/entry-component.ts](./projects/bookings/src/app/entry.component.ts)
156-
157-
```typescript
158-
import { EntryRoutingService } from 'ngx-elements-router';
159-
160-
@Component({
161-
selector: 'mf-angular-entry',
162-
template: `<router-outlet></router-outlet>`,
163-
})
164-
export class EntryComponent implements OnChanges, OnDestroy {
165-
@Input() route?: string;
166-
@Output() routeChange = new EventEmitter<string>();
167-
168-
route$ = new Subject<string | undefined>;
169-
170-
private readonly subscription: Subscription;
171-
172-
constructor(private entryRoutingService: EntryRoutingService) {
173-
this.subscription = this.entryRoutingService.registerRouting(
174-
this.routeChange,
175-
this.route$
176-
);
177-
}
178-
179-
ngOnDestroy(): void {
180-
this.subscription.unsubscribe();
181-
}
182-
183-
ngOnChanges() {
184-
this.route$.next(this.route);
185-
}
186-
```
187-
188-
### Create a custom element from the entry component
189-
190-
The module in your micro frontend needs to define the custom element in the browser on bootstrap of the module.
191-
192-
[micro-frontend/app-module.ts](./projects/bookings/src/app/app.module.ts)
193-
194-
```typescript
195-
import { EntryComponent } from "./entry.component";
196-
import { createCustomElement } from "@angular/elements";
197-
198-
@NgModule({
199-
declarations: [EntryComponent],
200-
imports: [BrowserModule],
201-
providers: [],
202-
})
203-
export class AppModule {
204-
constructor(private injector: Injector) {}
205-
206-
ngDoBootstrap() {
207-
const customElement = createCustomElement(EntryComponent, {
208-
injector: this.injector,
209-
});
210-
window.customElements.define("mf-entry", customElement);
211-
}
212-
}
213-
```
214-
215-
### Create a component that sits at your micro frontend route
216-
217-
The priorly created `EntryComponent` gets the full path starting after the base href.
218-
In its router outlet, a `MicroFrontendComponent` is mounted if the full path is a path to the micro frontend.
219-
Inside this component, you can use an absolute path `/abc` to refer to a route outside of the micro frontend.
220-
You can use a relative path `./abc` to refer to a route relative to the micro frontend route.
221-
It can itself have a router outlet to mount different components depending on the subpath of the micro frontend.
222-
223-
[micro-frontend/micro-frontend.component.ts](./projects/bookings/src/app/micro-frontend.component.ts)
224-
225-
```typescript
226-
@Component({
227-
selector: "mf-micro-frontend",
228-
template: `
229-
<a routerLink="/child">/child</a>
230-
<a routerLink="./child">/micro-frontend/child</a>
231-
<router-outlet></router-outlet>
232-
`,
233-
})
234-
export class MicroFrontendComponent {}
235-
```
236-
237-
### Define the routes in the micro frontend
238-
239-
The route structure in the micro frontend needs to be defined with the same structure as the platform.
240-
If the platform delegates all traffic at `/micro-frontend` to the micro frontend, then the micro frontend should define such a route.
241-
All other traffic needs to go to a route `**` such that the router module of the micro frontend does not discard it as undefined routes.
242-
This way, you can navigate to links outside of the micro frontend from within the micro frontend.
243-
244-
[micro-frontend/app-routing.module.ts](./projects/bookings/src/app/app-routing.module.ts)
245-
246-
```typescript
247-
import { NoComponent } from "ngx-elements-router";
248-
249-
const routes: Routes = [
250-
{
251-
path: "micro-frontend",
252-
component: MicroFrontendComponent,
253-
children: microfrontendRoutes,
254-
},
255-
{ path: "**", component: NoComponent },
256-
];
257-
```
258-
259-
### Prevent direct access to the browser url
260-
261-
By default, the Angular router within the micro frontend tries to update the browser url.
262-
Use the `NoopLocationStrategy` to prevent this, such that the platform has the only access.
263-
264-
[micro-frontend/app-routing.module.ts](./projects/bookings/src/app/app-routing.module.ts)
265-
266-
```typescript
267-
import { NoopLocationStrategy } from "ngx-elements-router";
268-
269-
@NgModule({
270-
imports: [RouterModule.forRoot(routes)],
271-
providers: [{ provide: LocationStrategy, useClass: NoopLocationStrategy }],
272-
exports: [RouterModule],
273-
})
274-
export class AppRoutingModule {}
275-
```
276-
277-
### Setup a dev platform within the micro frontend
278-
279-
For the independent development of the micro frontend, a minimal dev platform consisting of an index.html with some Javascript can be of advantage.
280-
This dev platform can be used both locally and also be deployed and used together with the bundle.
281-
282-
To use it, include the `dev-platform.js` in the scripts of your micro frontend in the `angular.json`.
283-
284-
[angular.json](./angular.json)
285-
286-
```json
287-
{
288-
"build": {
289-
"builder": "ngx-build-plus:build",
290-
"options": {
291-
"singleBundle": true,
292-
"outputHashing": "none",
293-
...,
294-
"scripts": [
295-
"node_modules/ngx-elements-router/src/dev-platform.js"
296-
]
297-
},
298-
}
299-
```
300-
301-
Setup an `index.html` in the micro frontend app.
302-
303-
[micro-frontend/index.html](./projects/bookings/src/index.html)
304-
305-
```html
306-
<!DOCTYPE html>
307-
<html lang="en">
308-
<head>
309-
<meta charset="utf-8" />
310-
<title>Example Micro Frontend</title>
311-
<base href="/" />
312-
<meta name="viewport" content="width=device-width, initial-scale=1" />
313-
<link rel="icon" type="image/x-icon" href="favicon.ico" />
314-
<script src="scripts.js"></script>
315-
</head>
316-
<body>
317-
<button onclick="router.changeRoute('/')">Go to platform main page</button>
318-
<button onclick="router.changeRoute('/micro-frontend')">
319-
Go to micro frontend main page
320-
</button>
321-
<div id="router-outlet"></div>
322-
<script>
323-
const router = registerRouting("/micro-frontend", "mf-entry");
324-
</script>
325-
</body>
326-
</html>
327-
```
328-
329-
### Pass Zone.js events
330-
331-
Zone.js registers itself globally on the window object.
332-
If the platform and the micro frontend are both Angular projects relying on Zone.js, then they concurrently access it and interfere with each others change detection cycles.
333-
To mitigate that, you can pass Zone.js microtask empty events to the micro frontend. These events are the trigger of a change detection cycle.
334-
335-
You can use the `EntryZoneService` in the Angular component representing the custom element.
336-
This way, Zone.js micro task empty events are passed to the micro frontend and a change detection cycle is triggered.
337-
338-
[micro-frontend/entry-component.ts](./projects/bookings/src/app/entry.component.ts)
339-
340-
```typescript
341-
import { EntryZoneService } from "ngx-elements-router";
342-
343-
@Component({
344-
selector: "mf-angular-entry",
345-
template: `<router-outlet></router-outlet>`,
346-
})
347-
export class ExampleComponent implements OnChanges, OnDestroy {
348-
@Input() microtaskEmpty$?: Observable<void>;
349-
microtaskEmpty$$ = new Subject<Observable<void>>();
350-
351-
constructor(private entryZoneService: EntryZoneService) {
352-
this.subscription = this.entryZoneService.registerZone(
353-
this.microtaskEmpty$$
354-
);
355-
}
356-
357-
ngOnDestroy() {
358-
this.subscription.unsubscribe();
359-
}
360-
361-
ngOnChanges() {
362-
this.microtaskEmpty$$.next(this.microtaskEmpty$);
363-
}
364-
}
365-
```
366-
367-
Use the `aerRouting` on the custom element to pass micro task empty events to the micro frontend.
368-
369-
[platform/micro-frontend-host.component.ts](./projects/train-platform/src/app/micro-frontend-host/micro-frontend-host.component.ts)
370-
371-
```typescript
372-
import { Component } from "@angular/core";
373-
374-
@Component({
375-
selector: "app-host",
376-
template: `<mf-entry aerZone></mf-entry>`,
377-
})
378-
export class MicroFrontendHostComponent {}
379-
```
380-
381-
## Limitations
382-
383-
Note that this library is a workaround to make the most common use cases of the Angular router accessible in a micro frontend.
384-
It has not been tried so far, if this approach works with more advanced features like named router outlets or router child modules.
385-
Various navigation options like `skipLocationChange` might not be propagated properly.
386-
Feel free to open issues or contribute pull requests for functionality that you think should be supported!
10+
A visit to `localhost:4200` shows the application.

0 commit comments

Comments
 (0)