@webruntime/navigation
v1.66.8
Published
LWR Client-side Navigation APIs
Downloads
1,434
Keywords
Readme
Lightning Web Runtime :: Navigation and Routing
Table of contents
- Introduction
- Background
- Webruntime Routing APIs
- Customize a router
- Nesting routers
- Using Router Views
- Lightning Navigation
- Advanced topics
- Setup
- Architecture
Introduction
This is a client-side routing package for off-core LWC-based applications, from Lightning Web Runtime (Webruntime).
It supplies an API with the ability to create a router, navigate, generate URLs and subscribe to navigation events.
Routers can be customized at various plug points, or used as-is. Routers can also be nested, to create a hierarchy on the page.
Built on top of the Webruntime routing APIs is a Lightning Navigation layer. It provides implementations for the NavigationMixin
and CurrentPageReference
wire adapter from lightning/navigation. This allows a component to be written once, and plugged in anywhere that uses the lightning/navigation
contracts.
A consumer of this package can write an application that uses the Webruntime routing APIs, the Lightning Navigation layer, or both. Customer components or applications will most likely use the Lightning Navigation API, as our public-facing contract.
Background
Definitions
These concepts are used throughout this documentation. All examples are using this URL:
https://www.somewhere.com/case/10?param1=one¶m2=two¶m3
- URL: A browser URL, either absolute or relative, containing both the path and query parameters (e.g.:
https://www.somewhere.com/case/10?param1=one¶m2=two¶m3
or/case/10?param1=one¶m2=two¶m3
) - path: The
pathname
of a URL (e.g.:/case/10
) - absolute path: A path that contains the entire
pathname
of a URL (e.g.:/case/10
NOT/10
) - query string: The query part of a URL (e.g.:
?param1=one¶m2=two¶m3
) - query object: An object representing a parsed query string, e.g.:
{
"param1": "one",
"param2": "two",
"param3": ""
}
- route definition: An object used to describe a URL category or pattern. A shape containing a unique
id
and an optionally parameterizedpath
is used. Path-to-regexp is used to parse thepath
. To supportlightning/navigation
APIs, add thepage
property. Additional custom data may also be included, e.g.:
{
/* required data */
"path": "/case/:recordId/:optional?", // Uses path-to-regexp for parsing parameters.
"exact": true/false, // When true, will only match if the path matches location.pathname exactly. The default is `true`.
/* Basic routing */
"id": "case-detail",
/* lightning/navigation */
"page": {
"type": "standard__recordPage",
"attributes": {
"objectApiName": "Case"
}
},
/* custom data */
"view": "caseDetail"
"label": "Case Detail"
}
Note: More information about route definition matching can be found here.
- route: An object representing the current URL. The
id
comes from the matching route definition. Theattributes
property contains the parameters parsed from the URL using the route definitionpath
. Thestate
property contains the query object, e.g.:
{
"id": "case-detail",
"attributes": {
"objectApiName": "Case",
"recordId": "place"
},
"state": {
"param1": "one",
"param2": "two",
"param3": ""
}
}
- page reference: A special type of route object used for
lightning/navigation
. Thetype
property is used instead of theid
, e.g.:
{
"type": "standard__recordPage",
"attributes": {
"objectApiName": "Case",
"recordId": "place"
},
"state": {
"param1": "one",
"param2": "two",
"param3": ""
}
}
- simple route: When a URL does not match any route definitions, a default route containing the absolute path and query object is produced by the router, e.g.:
{
"type": "standard__simpleRoute",
"attributes": {
"path": "/case/10"
},
"state": {
"param1": "one",
"param2": "two",
"param3": ""
}
}
- navigation event: The navigation state is changing, either by a browser URL update, or programmatically.
- navigation context: When a component provides navigation context, it implements the functions necessary to support the Webruntime routing APIs for its descendent components.
- router: A piece of code that manages navigation changes. A router can have up to 1 direct child router. A router provides navigation context.
- root router: The router instance that sits highest up in the DOM hierarchy. Updates flow from the root router to its child router, then grandchild router, etc. Some applications may only have a root router.
- translation layer: The functions a router uses to translate a URL to a route, and a route to a URL.
Prerequisites
The navigation package has a dependency upon the LWC wire-service. To include the wire-service
:
- Preload the
wire-service
inwebruntime-app.config.js
:
preloadModules: ['wire-service'],
- Register the
wire-service
in the root component of the application:
import { LightningElement, register } from 'lwc';
import { registerWireService } from 'wire-service';
registerWireService(register);
export default class RootApp extends LightningElement {
// ...
}
Webruntime routing APIs
The Webruntime routing APIs supply functions to create a root router, navigate (programmatically and declaratively), generate URLs, and subscribe to navigation events.
// app.js
import { createRouter, navigate, generateUrl, subscribe, NavigationContext } from 'webruntime/navigation';
<!-- component.html -->
<webruntime-link path="/some/where">CLICK ME</webruntime-link>`
createRouter()
const router = createRouter(config);
Create a new root router for an application with arguments:
config
: Configure the router with an object containing these properties:basePath
: Set a base path for this router. The default is an empty string.routes
: Set an array of route definitions for URL parsing. The default is an empty array.noHistory
: By default, the router will manage the browser history. To turn this behavior off, setnoHistory: true
.caseSensitive
: Whentrue
, the route definition path matching will be case sensitive. The default isfalse
.
A router object is returned:
connect()
: Call this to start the router.disconnect()
: Call this to stop the router from interacting with the application.addPreNavigate(function | function[]) -> this router
: Add a listener function, or array of functions, to the pre navigate event hook.addPostNavigate(function | function[]) -> this router
: Add a listener function, or array of functions, to the post navigate event hook.addErrorNavigate(function | function[]) -> this router
: Add a listener function, or array of functions, to the navigation error hook.id
: The navigation context ID for this router.
Lifecycle hooks
A router can have 0 or more listeners attached to each of its lifecycle hooks. The listeners are fired synchronously in the order in which they were attached:
preNavigate(transaction)
: This runs during a navigation event, before the state is changed. Listeners can stop the transaction at this point. If stopped, theerrorNavigate
listeners will be fired. ThepreNavigate
functions should return one of these values:true
: The route should be processed.false
: The navigation event should be stopped, and no more listeners run on any router.Promise
: Resolves to one of the values above; rejectedPromise
s are treated asfalse
.
postNavigate(transaction)
: This runs after a navigation event completes. Subscribers will not be notified until all post navigation listeners have finished executing. If apostNavigate
listener returnsfalse
or a rejectedPromise
, the remaining listeners for that router are skipped, but thepostNavigate
listeners on any descendent routers will still run.errorNavigate(error)
: This runs if the router encounters an error trying to navigate.
The pre and post navigation listens are passed a read-only transaction object:
current
: The navigation information{ route, data }
for the current state, route may benull
duringpreNavigate
.next
(preNavigate
only): The navigation information that will load next (if not stopped).previous
(postNavigate
only): The navigation information that just unloaded, route may benull
.
The postNavigate
listeners are passed an error
object:
{
code: an integer error code,
message: a string error message,
level: 1,
url: an optional URL where more information on the error can be found
}
See an example here.
NavigationContext
@wire(NavigationContext)
navContext;
A wire adapter that gives a component access to the its navigation context. The navigation context is used to call the Webruntime routing APIs below.
navigate()
navigate(navigationContext, location, shouldReplace);
Navigate to a new page programmatically with arguments:
navigationContext
: The navigation context in which to navigate.location
: A route, or absolute path, to navigate to.shouldReplace
: Sendtrue
if the new location should replace the current one in the browser history. The default isfalse
.
generateUrl()
generateUrl(navigationContext, route).then((url) => this.path = url);
Given a route, returns a Promise
to a string URL.
subscribe()
const callback = (route, data) => console.log(route, data);
this.subscription = subscribe(navigationContext, callback);
// .....
if (this.subscription) {
this.subscription.unsubscribe();
}
Pass in a callback function that will get executed every time the navigation state changes. Receive a Promise
to an observer with an unsubscribe()
function in return.
The callback function takes these arguments:
route
: The new current route.data
: Extra data associated with the current route. In the default implementation, this is the route definition which matches the current route. It will be null in the case of Simple Route (i.e.: no match).
webruntime-link
A LWC used for declarative navigation. It can take either a string URL or a route as input.
<webruntime-link path="/a/path?filter=all">
<span>CLICK HERE</span>
</webruntime-link>
<webruntime-link route="{route}">
<img src="/my/link.gif" alt="click me" />
</webruntime-link>
The following CSS variables are used to style the internal a
tag. Overwrite the values to update the look of the link
s.
a {
color: var(--webruntime-link-color);
font-size: var(--webruntime-link-font-size);
text-decoration: var(--webruntime-link-text-decoration);
}
a:hover,
a:active {
color: var(--webruntime-link-color-active, var(--webruntime-link-color));
font-size: var(--webruntime-link-font-size-active, var(--webruntime-link-font-size));
text-decoration: var(--webruntime-link-text-decoration-active);
}
Usage examples
Creating a root router with hooks
import { createRouter } from 'webruntime/navigation';
const ROUTE_DEFINITIONS = [
{
id: 'recordPage',
path: '/r/:objectApiName/:recordId/:actionName',
},
{
id: 'objectPage',
path: '/o/:objectApiName/:actionName',
},
{
id: 'homePage',
path: '/', // This is the default route, and it must be last.
},
];
const router = createRouter({ basePath: '/demo', routes: ROUTE_DEFINITIONS });
// Add logger pre and post navigation hook listeners. Then connect.
router
.addPreNavigate((t) => console.log(`pre: Current: %o. Next: %o`, t.current.route, t.next.route))
.addPostNavigate((t) =>
console.log(`post: Current: %o. Previous: %o`, t.current.route, t.previous.route)
)
.addErrorNavigate((e) =>
console.error(`There was a problem during navigation: ${e.code} :: ${e.message}`)
)
.connect();
Using the webruntime/navigation
APIs
import { track, wire, LightningElement } from 'lwc';
import { navigate, generateUrl, subscribe, NavigationContext } from 'webruntime/navigation';
const aRoute = {
id: 'page',
attributes: {
name: 'sample',
},
};
export default class Example extends LightningElement {
@track name = '';
@track path = null;
subscription = null;
// Get a reference to the navigation context for this component.
@wire(NavigationContext)
navContext;
// Subscribe to updates on the current state.
connectedCallback() {
this.subscription = subscribe(this.navContext, (route, data) => {
this.name = route.attributes.name || '';
this.path = data ? data.path : null;
});
}
// Navigate programmatically by URL.
navUrl() {
navigate(this.navContext, '/some/path');
}
// Navigate programmatically by route.
navRoute() {
navigate(this.navContext, aRoute);
}
// Generate a URL for a route.
getUrl() {
generateUrl(this.navContext, aRoute).then((url) => console.log(url));
}
// Disconnect from the navigation event subscription.
disconnectedCallback() {
if (this.subscription) {
this.subscription.unsubscribe();
}
}
}
Customize a router
A router has plug-points that accept logic to override the default behavior. The translation layer (route <-> URL) and navigation event handling can be customized. Pass the plug-points in as properties on a router's config object.
Translation layer plug-points
- getRouteFromUrl:
f(url, defaultImpl) -> { route, data }
: Given a URL (absolute or relative), return the associated route and data. If a route could not be found to match the given URL thenroute: null
. - getUrlFromRoute:
f(route, defaultImpl) -> { url, data }
: Given a route, return the associated relative URL string with an absolute path and data. If a URL could not be created for the given route thenurl: null
.
Things to note:
- Returning
null
for aurl
orroute
will result in theerrorNavigation
hook listeners being call on all routers. - If a plug-point is not provided, a default implementation is used. The default implementation function is provided as a second argument to the plug-point functions for convenience.
- The default implementation of
getRouteFromUrl
will never returnroute: null
. A Simple Route is returned for URLs that do not match any route definition. See this example for turning off Simple Routes.
Navigation event plug-points
- handleNavigation:
f(url | route, shouldReplace) -> boolean
: This is called whenever a navigation event bubbles up through a router. Return false to stop propagation and cancel the event. This function is a no-op by default. The arguments are:url
|route
: The string URL or route object passed in fromnavigate()
.shouldReplace
:true
if the URL should replace the current one in the browser history.
Usage examples
Customizing a root router
import { LightningElement } from 'lwc';
import { createRouter } from 'webruntime/navigation';
import ROUTE_DEFINITIONS from './routeDefinitions';
export default class Example extends LightningElement {
constructor() {
super();
this.router = createRouter({
basePath: '/demo',
routes: ROUTE_DEFINITIONS,
handleNavigation: this.handleNavigation.bind(this),
getRouteFromUrl: this.getRouteFromUrl.bind(this),
getUrlFromRoute: this.getUrlFromRoute.bind(this),
});
this.router.connect();
}
disconnectedCallback() {
this.router.disconnect();
}
// Process a navigation event bubbling up from descendent components.
// The getRouteFromUrl function is provided for convenience.
// This example stops propagation if the route id is 'no__good'.
handleNavigation(input, options, getRouteFromUrl) {
const route = typeof input === 'string' ? getRouteFromUrl(input).route : input;
return route.id !== 'no__good';
}
// Return null as the route instead of Simple Routes.
// This turns off the Simple Route feature.
// The errorNavigate hook listeners are run when { route: null }
getRouteFromUrl(url, defaultImpl) {
const defaultInfo = defaultImpl(url);
return defaultInfo.route.type === 'standard__simpleRoute'
? { route: null, data: null }
: defaultInfo;
}
// Set a sticky query parameter on every URL.
getUrlFromRoute(route, defaultImpl) {
const { url, routeDef } = defaultImpl(route);
return {
url: url ? `${url}?new=param` : null,
data: routeDef,
};
}
}
Nesting routers
Multiple routers can be nested, with each router having up to 1 child. Each router is responsible for parsing part of the URL path, and providing the associated route to its descendent subscribers.
webruntime-router
<webruntime-router
routes="{routes}"
base-path="/base"
no-history
case-sensitive
onprenavigate="{preNavigate}"
onpostnavigate="{postNavigate}"
onerrornavigate="{errorNavigate}"
onhandlenavigation="{handleNavigation}"
></webruntime-router>
Create a child router declaratively with this LWC. Pass the config options to the router via @api properties:
routes
base-path
no-history
case-sensitive
Attach hooks with event listeners:
onprenavigate
is cancelableonpostnavigate
onerrornavigate
onhandlenavigation
is cancelable
Usage examples
Create and customize a webruntime-router
<template>
<webruntime-router
routes="{routes}"
base-path="/demo"
onprenavigate="{preNavigate}"
onpostnavigate="{postNavigate}"
onerrornavigate="{errorNavigate}"
onhandlenavigation="{handleNavigation}"
>
<template if:true="{message}"><error>{message}</error></template>
<webruntime-link path="/a/place">I'm inside the child navigation context</webruntime-link>
<view></view>
</webruntime-router>
</template>
import { track, LightningElement } from 'lwc';
import ROUTE_DEFINITIONS from './routeDefinitions';
import { user } from '../services/user';
import { getData } from '../services/xhr';
export default class Example extends LightningElement {
@track errorMessage = null;
routes = ROUTE_DEFINITIONS;
// Add lifecycle hook events.
preNavigate(e) {
e.stopPropagation();
// If any pre-navigate hook returns false, cancel the event.
if (!this.authorization(e.detail) || !this.unmountCheck(e.detail)) {
e.preventDefault();
}
}
postNavigate(e) {
e.stopPropagation();
this.logNav(e.detail);
this.getData(e.detail);
}
errorNavigate(e) {
e.stopPropagation();
this.showError(e.detail);
this.getData(e.detail);
}
handleNavigation(e) {
e.stopPropagation();
const { input } = e.detail;
console.log('Bubbling navigation event: ', input);
}
// Do not allow navigation to private pages for guest users.
authorization(transaction) {
if (user.isGuest() && transaction.next.route.id === 'private-page') {
return false;
}
return true;
}
// Warn users before leaving the current page.
// !transaction.current.route indicates a first time page load.
unmountCheck(transaction) {
return !transaction.current.route
? true
: new Promise((resolve) => {
resolve(confirm('Are you sure you want to leave?'));
});
}
// Log navigation events that just completed.
// Clear the error message.
logNav(transaction) {
console.log(
`postNav -> Current: %o. Previous: %o`,
transaction.current.route,
transaction.previous.route
);
this.errorMessage = null;
}
// Get some XHR data before the subscribers are notified of this navigation event.
getData(transaction) {
return getData(transaction.current.route.id).then((data) => doSomething(data));
}
// Show navigation errors to the user.
showError(e) {
this.errorMessage = `There was a problem during navigation: ${e.message}`;
}
}
Root router and webruntime-router
relationship
When a child router is added, there are two navigation context providers present in the application. Consider a root router with these properties:
basePath: '/lightning';
routes: [
{
id: 'app',
path: '/app/:appName',
exact: false, // This allows the child router at this route definition
},
];
a child router with:
basePath: '';
routes: [
{
id: 'page',
path: '/page/:name',
},
];
and a URL like this:
/lightning/app/cat+app/page/siamese?cute=yes
A successful navigation event looks like this:
- A navigation event is detected, and the
preNavigate
hooks are fired from root -> leaf router node. - The root router matches the first 3 segments of the path and produces this route:
{
"id": "app",
"attributes": {
"appName": "cat app"
},
"state": {
"cute": "yes"
}
}
- The route is sent to the subscribers of the root router navigation context.
- The root router delegates to the child router by sending it the absolute path of the URL.
- The child router matches the last 2 segments of the path and produces this route:
{
"id": "page",
"attributes": {
"name": "siamese"
},
"state": {
"cute": "yes"
}
}
- The
postNavigate
hooks are fired on the root and its subscribers are notified. - The
postNavigate
hooks are fired on the child and its subscribers are notified.
Note: Subscribers receive different data depending on their navigation context, provided by the closest ancestor router.
Using Router Views
The LWR Routing Service allows a lwc to be specified for each route definition. LWR also provides a component which automatically displays the lwc associated with each route. To use the Routing Service, add it to the services
array in webruntime-app.config.js:
const { RoutingService } = require('@webruntime/navigation');
module.exports = {
services: [RoutingService],
};
Setup route definitions
Create one or more JSON files in a LWR project to hold route defintion data:
projectRoot
├── routes/
│ └── cooking.json // route set id = "cooking"
│ └── child.json // route set id = "child"
Note: Multiple files are created to support nested routers.
Each file holds a "route set" with an ID matching the filename. The route set JSON looks like this:
{
"home": {
"path": "/",
"component": "x/home"
},
"recipe": {
"path": "/recipes/:title",
"component": "x/recipeItem"
}
}
Note: The route definition IDs are keys in this object. This automatically supports ID uniqueness across the route set.
Generate a router
The LWR Routing Service generates a router component, given a route set ID:
<!-- x/app template -->
<template>
<!-- Add a router to the application template
This router receives the data from cooking.json above -->
<webruntime-router-cooking></webruntime-router-cooking>
</template>
The webruntime-router-{setID}
components support the same properties and events as the webruntime-router
component, besides routes
which is passed automatically given the route set ID.
Add a router outlet
The route sets contain a component for each route definition. Add a webruntime-outlet
component under a router to automatically display the component on route change:
<!-- x/app template -->
<template>
<!-- Add a router to the application template
This router receives the data from cooking.json above -->
<webruntime-router-cooking>
<!-- Add an outlet as a child to the router
This renders the current component view -->
<webruntime-outlet>
<span slot="error">
<x-error></x-error>
</span>
<span slot="404">
<x-404></x-404>
</span>
</webruntime-outlet>
</webruntime-router-cooking>
</template>
The webruntime-outlet
component contains:
- slots:
- "error": Shows when there is an error loading the component for the route.
- "404": Shows when the component could not be resolved.
- properties:
refocus-off
: The outlet automatically puts focus on the component when it is loaded, for accessibility. To turn this feature off, add therefocus-off
property towebruntime-outlet
The attributes for the current route are automatically passed into the corresponding component as public properties (@api
). Given this route:
{
"id": "recipe",
"attributes": {
"title": "bread"
}
}
and the "x/recipeItem" component:
import { LightningElement, api } from 'lwc';
export default class XRecipeItem extends LightningElement {
@api title;
}
the router outlet would pass in "bread" for the title property:
<x-recipe-item title="bread"></x-recipe-item>
Lightning Navigation
The webruntime/lightningNavigation
package provides implementations for the NavigationMixin
and CurrentPageReference
wire adapter from lightning/navigation.
import { NavigationMixin, CurrentPageReference } from 'webruntime/lightningNavigation';
CurrentPageReference
A wire adapter that gives a component access to the current page reference (relative to its navigation context).
NavigationMixin
A JavaScript class mixin that provides functions to navigate or generate a URL:
this[NavigationMixin.Navigate](url | pageRef)
: Programmatically navigate to a string URL or page reference.this[NavigationMixin.GenerateUrl](pageRef) => Promise<string>
: Translate a page reference into an absolute path.
Usage examples
NavigationMixin
and CurrentPageReference
This example is analogous to the webruntime/navigation
API example.
import { wire, LightningElement } from 'lwc';
import { NavigationMixin, CurrentPageReference } from 'webruntime/lightningNavigation';
const aPageRef = {
type: 'page',
attributes: {
name: 'sample',
},
};
export default class Example extends NavigationMixin(LightningElement) {
// Subscribe to updates on the current state.
@wire(CurrentPageReference)
currentPageRef;
// Use the current page reference.
@track
get name() {
return this.currentPageRef ? this.currentPageRef.attributes.name : '';
}
// Navigate by URL.
navUrl() {
this[NavigationMixin.Navigate]('/some/path');
}
// Navigate by page reference.
navPageRef() {
this[NavigationMixin.Navigate](aPageRef);
}
// Generate a URL.
getUrl() {
this[NavigationMixin.GenerateUrl](aPageRef).then((url) => console.log(url));
}
}
Advanced topics
provideContext()
provideContext(context, node)
Using the createRouter()
API will create a Router
with navigation context automatically. To create custom navigation context, call the provideContext()
API with the following arguments:
context
: The provided context must have exactly these properties:navigate
: An implementation of thenavigate()
function.generateUrl
: An implementation of thegenerateUrl()
function.subscribe
: An implementation of thesubscribe()
function.
An object is returned containing these properties:
id
: An identifier for the navigation context. This is the value returned over theNavigationContext
wire adapter.update(value)
: Update thecontext
object to a new value. Subscribers will be updated with the new API implementations.disconnect()
: Stop the navigation context from being detected by descendant components.
Hardcoding context
In a case where an application only has one router, the application developers can consider providing their users with Navigation APIs which are locked to the single navigation context. Then users do not need to pull in the navigation context themselves.
import {
navigate as webruntimeNavigate,
generateUrl as webruntimeGenerateUrl,
subscribe as webruntimeSubscribe,
createRouter
} from 'webruntime/navigation';
// Create and start the router.
const router = createRouter({...});
// Navigate programmatically.
export function navigate(loc, options) {
webruntimeNavigate(router.id, loc, options);
}
// Generate a URL for the given route.
export function generateUrl(route) {
return webruntimeGenerateUrl(router.id, route);
}
// Subscribe to navigation state changes.
export function subscribe(callback) {
webruntimeSubscribe(router.id, callback);
}
Setup
Install
yarn add @webruntime/navigation
Build
Build tasks can be run in this repository from the /packages/@webruntime/navigation directory with:
yarn clean
yarn build
Tests
Test tasks can be run in this repository from the /packages/@webruntime/navigation directory with:
yarn test // run jest tests
yarn coverage // test with coverage output
yarn lint // lint the code
Architecture
Static analysis
A routing goal is to be able to statically analyze by URL path. Meaning, the code needed to display a component/view/page for a given path can be known ahead of runtime. Route definitions should contain the metadata needed for a builder plugin to do the analysis. A different builder plugin is written to analyze different styles of route definition metadata. Example route definitions:
- Basic id-based routing:
{
"id": "home",
"path": "/home",
"component": "our-home"
}
- Lightning Navigation page-based:
{
"path": "/case/:recordId",
"page": {
"type": "standard__recordPage",
"attributes": {
"objectApiName": "Case"
}
}
}
- Webruntime view-based, with Lightning Navigation support:
{
"id": "path-detail",
"path": "/case/:recordId",
"view": "caseDetail",
"label": "Case Detail",
"page": {
"type": "standard__recordPage",
"attributes": {
"objectApiName": "Case"
}
}
}
Route definition matching
Matching a route to a route definition is relatively straight forward, since the id
s are unique. Here is the criteria:
route.id === routeDef.id
- each non-optional parameterized path segment (e.g.:
/:recordId
) in the route definitionpath
, must match a key in the routeattributes
Here is an example:
// route definition
{
"id": "home",
"path": "/app/:appName"
}
// matching route
{
"id": "home",
"attributes": {
"appName": "awesome"
}
}
// NOT matching route
{
"id": "home"
}
Matching a page reference is more complex. Here is the criteria:
pageRef.type === routeDef.page.type
- each non-optional parameterized path segment (e.g.:
/:recordId
) in the route definitionpath
, must match a key in the page referenceattributes
- each key/value pair in the route definition
page.attributes
, must match a key/value pair in the page referenceattributes
- if there is more than 1 route definition match for a page reference, pick the first one
Here are some examples:
- simplest case, no
attributes
:
// page reference
{
"type": "home"
}
// matching route definition
{
"path": "/home",
"page": {
"type": "home"
}
}
// NOT matching route definition
{
"path": "/old/home",
"page": {
"type": "home"
}
}
- route definition with parameterized
path
:
// page reference
{
"type": "standard__recordPage",
"attributes": {
"recordId": "0D50M00004NgNxtSAF"
}
}
// matching route definition
{
"path": "/record/:recordId",
"page": {
"type": "standard__recordPage"
}
}
- route definition with parameterized
path
containing optional parameters:
// page reference
{
"type": "standard__recordPage",
"attributes": {
"recordId": "0D50M00004NgNxtSAF"
}
}
// matching route definition
{
"path": "/record/:recordId/:recordName?",
"page": {
"type": "standard__recordPage"
}
}
- route definition has
page.attributes
:
// page reference
{
"type": "standard__objectPage",
"attributes": {
"objectApiName": "Case"
}
}
// matching route definition
{
"path": "/cases",
"page": {
"type": "standard__objectPage",
"attributes": {
"objectApiName": "Case"
}
}
}
- route definition has both a parameterized
path
andpage.attributes
:
// page reference
{
"type": "standard__recordPage",
"attributes": {
"objectApiName": "Case",
"recordId": "0D50M00004NgNxtSAF"
}
}
// matching route definition
{
"path": "/case/:recordId",
"page": {
"type": "standard__recordPage",
"attributes": {
"objectApiName": "Case"
}
}
}
Diagrams
Programmatic navigation events flow up from their source node through each navigation context. If not stopped, they eventually reach the root router, which processes the event.
The URL is then parsed starting at the root router and flowing down through any child routers. First the preNavigate
hook listeners are run, from root to leaf. If all return true, the postNavigate
hook listeners are run, then each router hydrates their subscribers with a new route/page reference.