Plugin Navigation
Microfrontend Composer is a set of micro-frontend plugins. An orchestrator of micro-frontends should provide a mechanism for page/plugin navigation.
In this regard Back-Kit
provides a set of webcomponents
which can handle navigation main features given that the micro-frontend orchestrator is
actively listening the pushState / replaceState events.
Let's take on a real life example. Two separate plugins represent a main "collection", rendered with a table and http-client to fetch data, and a "detail" overview, made with a card and an http-client which probably requires filtering on data to fetch.
A button is clicked to navigate to the detail plugin.
Since the orchestrator cannot use a location.href
otherwise it would be reloaded making the operation anti-pattern, we could use the history
API provided by the browser. On click we provide all information needed to change plugin.
- a context of data we would like to carry along (i.e. the unique
id
of the detail we're navigating to) - the relative url (possibly with queries)
The micro-frontend orchestrator will provide plugin swapping capabilities by replacing the sandboxed document in which is mounting plugins and the browser will
- modify the URL string
- insert the data context into
window.history.state
Back-Kit
provides two components that are suitable to handle the landing on a new plugin.
URL Parameters Adapter
The URL Parameters Adapter provides a URL mask to separate the plugin URL (handled by the micro-frontend orchestrator) and the rest of the pathname useful to scope the detail plugin
{
"tag": "bk-url-parameters",
"properties": {
"urlMask": "/detail/:id",
"redirectUrl": "/collection"
}
}
and if the :id
key is not found it may prevent further navigation and push back.
This component listens to the window.location.href
only
Interplay with CRUD Client and CRUD Lookup Client
While The URL Parameters Adapter sends a change-query event with payload {id: "<some id>"}
, the change-query
subscribers which are the CRUD Client and the CRUD Lookup Client will permanently modify their http fetching query by including a default search parameters formed as ?...&id=<some id>&...
.\
This URL editing will scope the entire page providing the concept of a "detail" layout with respect to the "collection" layout centered on a table.
State Adapter
The State Adapter provides the capability to inject pushState
data into the EventBus
like if the user already performed some operations.
If the button clicked on the "collection" plugin provides a special key __BK_INIT
(whose name is configurable), its all content will be parsed to the EventBus
window.history.state = {
__BK_INIT: [
{
label: "add-new",
payload: {
detailId: "624ecd5642247867845498fb"
}
}
]
}
in the previous example EventBus
will pipe an add-new
event with the given detailId
payload. The corresponding action on a Button embedded into the "collection" plugin would be
{
"tag": "bk-button",
"properties": {
"content": "Add new item",
"action": {
"type": "push",
"config": {
"url": "/detail",
"state": {
"__BK_INIT": [
{
"label": "add-new",
"payload": {"detailId": "<some kind of reference>"}
}
]
}
}
}
}
}
Example - food delivery
Suppose you have food delivery orders listed in the main "collection" using a table.
The main table expects a field named orderId
as primary table index. A form is used to create a new
order
// orders-list.json
{
{
"tag": "bk-table",
"properties": {
"dataSchema": {
"type": "object",
"properties": {
"orderId": { "type": "string" }
}
}
}
},
{
"tag": "bk-add-new-button"
},
{
"tag": "bk-form-drawer",
"properties": {
"dataSchema": "<as above>",
"afterFinishEvents": {
"data": {
"__BK_INIT": [
{
"label": "add-new",
"payload": {"orderId": "{{response.orderId}}"}
}
]
},
"url": "/order-details/{{response.orderId}}"
}
}
}
}
On form submission, if the creation POST is, successful a context is provided with the HTTP response context to the afterFinishEvents.
According to the configuration shown above a pushState
is called and navigation to /order-details/<new id>
is handled by the micro-frontend orchestrator.
On landing onto order-details
plugin, we should focus on the following config
// order-details.json
{
{
"tag": "bk-url-parameters",
"properties": {
"eventLabel": "change-query",
"urlMask": "/order-details/:_id",
"redirectUrl": "/orders-list"
}
},
{
"tag": "bk-state-adapter"
},
{
"tag": "bk-form-drawer",
"properties": {
"dataSchema": {
"type": "object",
"properties": {
"orderId": { "type": "string" }
}
}
}
}
}
The URL Parameters Adapter will attempt current URL matching against the given mask. If it fails it will redirect according to the provided property.
Otherwise it will attempt to send the matched content to the EventBus
using a property called
eventLabel
which defaults to change-query.
Hence, let's suppose we land on /order-details/624ecd5642247867845498fb
, The URL Parameters Adapter will
send a change-query
event with payload
const payload = {
_id: "624ecd5642247867845498fb"
}
where _id
is taken from the urlMask
configuration.
Meanwhile, The State Adapter awaits a given delay timeout and then checks the window.history.state
which was injected when pushState
was called and if it finds a key __BK_INIT
(which is overridable)
and then pipe the array content to the EventBus
.
According to the incoming config the EventBus
will receive an add-new
event with payload
given by {orderId: "624ecd5642247867845498fb"}
.
The form drawer into the "details" plugin will then open by subscribing the add-new
event and
thus using the body to prefill the orderId
field.