This is a collection of advanced use cases and example implementations. If you are new to Luigi, take a look at our Getting Started section first.
This example shows you how to keep an existing routing strategy and use an existing micro frontend as drop-in without the need to refactor everything to LuigiClient.linkManager()
. To update the Luigi Core URL when routing internally with the micro frontend router, without updating the URL on the Luigi Client side, use the linkManager()
withoutSync and fromVirtualTreeRoot methods.
If you are running Luigi Core v0.7.7+, you can use fromClosestContext instead of fromVirtualTreeRoot
, which requires a navigationContext at the virtualTree
node configuration.
NOTE: This is a very simple example. For cases like modals or split views, you still require the use of Luigi Client.
- Configure the Luigi navigation node:
NOTE: To keep the example simple, we use virtualTree to allow any nested navigation, but this is not mandatory. You can always specify the node tree yourself and still use automatic navigation with router events.
{
pathSegment: 'Orders',
label: 'orders',
viewUrl: 'https://orders.microfrontend/',
virtualTree: true
}
- Use an Angular Router for navigation.
Angular provides Router events. We are reacting on NavigationEnd
to update the URL after a successful route change.
We assume that the whole Angular app is one micro frontend and has its routes declared on the root level:
{ path: 'preload', component: PreloadComponent },
{ path: '', component: OrderListComponent },
{ path: ':id', component: OrderComponent },
{ path: ':id/details', component: OrderDetailsComponent },
Use this code to implement luigi-auto-navigation.service.ts
, which is globally imported in our app.module.ts
:
import { Router, NavigationEnd } from '@angular/router';
import { Injectable, OnDestroy } from '@angular/core';
import { Subscription } from 'rxjs';
import { filter } from 'rxjs/operators';
import { linkManager } from '@luigi-project/client';
@Injectable({ providedIn: 'root' })
export class LuigiAutoNavigationService implements OnDestroy {
private subscriptions: Subscription = new Subscription();
constructor(private router: Router) {
this.subscriptions.add(
router.events
.pipe(filter(ev => ev instanceof NavigationEnd))
.subscribe((ev: NavigationEnd) => {
linkManager()
.fromVirtualTreeRoot()
.withoutSync()
.navigate(ev.url);
})
);
}
ngOnDestroy(): void {
this.subscriptions.unsubscribe();
}
}
app.module.ts
:
@NgModule({
providers: [LuigiAutoNavigationService],
Other than the added service, which you can also implement as a RouteGuard
or similar, the micro frontend is unchanged and uses [routerLink='']
or other functionality to navigate.
This example shows you how to use Luigi with a Google account.
- Register a project and generate an OAuth2 Web Client based on Google Developers Identity - OAuth2UserAgent.
- To get your app running locally, set the Authorized JavaScript Origins URIs to
http://localhost:[PORT]
(replace PORT by the port of your locally running luigi app, e.g.4200
for Angular). Then, set Authorized redirect URIs tohttp://localhost:[PORT]/luigi-core/auth/oauth2/callback.html?storageType=localStorage
. - Copy the Client ID which ends with
apps.googleusercontent.com
. - Update the LuigiConfig auth section. In this example, we have also provided a configuration for logout and getting user information:
{
auth: {
use: 'oAuth2ImplicitGrant',
oAuth2ImplicitGrant: {
authorizeUrl: 'https://accounts.google.com/o/oauth2/v2/auth',
oAuthData: {
response_type: 'id_token token',
client_id: 'YOUR_CLIENT_ID...apps.googleusercontent.com',
scope: 'openid https://www.googleapis.com/auth/userinfo.email profile',
}
},
logoutFn: async (settings, authData, logoutCallback) => {
console.log('revoking token');
await fetch(`https://accounts.google.com/o/oauth2/revoke?token=${authData.accessToken}`);
logoutCallback('/logout.html');
}
}
}
Google's id_token
contains basic identity data like name and user ID, which allows for this data to be shown in the profile.
5. If you would also like to show the user picture, add the following code to enrich the user profile information:
userInfoFn: async (settings, authData) => {
const response = await fetch('https://www.googleapis.com/oauth2/v1/userinfo', {
method: 'GET',
headers: {
'Authorization': 'Bearer ' + authData.accessToken
}
});
const json = await response.json();
return json;
},
There are two possibilities to add feature toggles to the active feature toggles list. On the one hand, you can use the Core API and on the other hand, it is possible to add a feature toggle through URL parameters.
Luigi allows you to implement and configure feature toggles. They can be used to organize and compartmentalize your code.
-
Before using feature toggles, you first have to include the feature toggle query parameter in the general settings part of your Luigi configuration file. This allows you to enable setting the feature toggles via URL :
featureToggles = { queryStringParam: 'ft' };
-
To set feature toggles, you have two possibilities:
- Set feature toggles to the active feature toggle list through Luigi Core API:
Luigi.featureToggles().setFeatureToggle('ft1');
- Set feature toggles to the active feature toggle list via URL parameters by appending a comma-separated list of strings. The parameter name is the predefined featureToggles.queryStringParam :
http://localhost:4200/projects/pr1?ft=ft1,ft2
-
To unset feature toggles, you have to use the Core API:
Luigi.featureToggles().unsetFeatureToggle('ft1');
-
To restrict node visiblity with feature toggles: You can define a list of feature toggles for a particular top or left navigation node. For that you can use the visibleForFeatureToggles parameter in order to display the node for certain feature toggles. For example, this node will be visible if
ft1
is added to the active feature toggle list:{ category: { label: 'Feature Toggle: Settings 2', icon: 'action-settings' }, pathSegment: 'settings_ft', label: 'Project Settings 2', viewUrl: '/sampleapp.html#/projects/' + projectId + '/settings', icon: 'settings', visibleForFeatureToggles: ['ft1'] }
If you define a list of multiple feature toggles, the node will be restricted and it will be shown only if all of the specified feature toggles are set.
It is also possible to negate the visibility of a node by adding an exclamation mark at the beginning of the feature toggle name. In this example, the node is always visible except if
ft1
is set as an active feature toggle:{ category: { label: 'Feature Toggle: Settings 2', icon: 'action-settings' }, pathSegment: 'settings_ft', label: 'Project Settings 2', viewUrl: '/sampleapp.html#/projects/' + projectId + '/settings', icon: 'settings', visibleForFeatureToggles: ['!ft1'] }
-
To use feature toggles in a micro frontend: It is possible to restrict content in a micro frontend using feature toggles. The active feature toggle list is available in the Luigi Client API.
if (LuigiClient.getActiveFeatureToggles().includes('ft1')) { //display content }
Luigi Client allows you to navigate through micro frontends by using an intent-based navigation. This type of navigation decouples navigation triggers from the actual navigation targets. Rather than directly encoding the name of the target app into the URL fragment, app developers provide a navigation intent such as display
or edit
as shown in the examples below.
-
To enable intent-based navigation, you need to first identify the necessary target mappings. This can be done by defining
intentMapping
in the Luigi configuration undernavigation
as in the example below:intentMapping = [ { semanticObject: 'Sales', action: 'display', pathSegment: '/projects/sap/munich/database/sales/display' }, { semanticObject: 'Sales', action: 'edit', pathSegment: '/projects/sap/munich/database/sales/edit' } ];
- The intent link is built using the
semanticObject
,action
and optional parameters in the following format:#?intent=semanticObject-action?params
. An example of an intent link would be as follows:
#?intent=Sales-edit?id=100
- Navigation to a micro frontend through this intent is then made possible by using the linkManager navigate method from Luigi Client API:
LuigiClient.linkManager().navigate('#?intent=Sales-edit?id=100');
- This method would then be navigating to the translated real path segment:
https://example.com/projects/sap/munich/database/sales/edit?~id=100;
- Alternatively, the intent link can also be accessed through the browser URL and accessed from outside:
https://example.com/#?intent=Sales-edit?id=100;
- The intent link is built using the
In some scenarios, the micro frontend application needs to decide when to finalize the Luigi Client initialization. By default, Luigi Client is initialized when you import the library in your micro frontend application. However, it can be the case that a complex application takes too long to load all the modules. Since Luigi Client initialization is done automatically when it is imported, Luigi Core will assume that the micro frontend is fully loaded and ready for further actions when it is not. This may lead to some problems, such as UI synchronization issues where the side menu highlights an item, but the micro-frontend application shows different content.
These are the steps you can use to defer Luigi Client initialization :
- In your micro frontend HTML that serves as entry file, you must add the
defer-luigi-init
attribute into the<head>
element as follows:
<html>
<head defer-luigi-init>
....
</head>
.....
</html>
```
2. Then, you can use the Luigi Client API inside your micro frontend:
```javascript
LuigiClient.luigiClientInit();
NOTE: This will only initialize Luigi Client if it hasn't already been initialized.