Skip to content

Commit

Permalink
Merge pull request #115 from Banno/dynamic-import
Browse files Browse the repository at this point in the history
Dynamic importFunction on routeData
  • Loading branch information
chrisgubbels authored Dec 11, 2024
2 parents 90106a2 + 4dfeaee commit 9a75bbb
Show file tree
Hide file tree
Showing 7 changed files with 17 additions and 7 deletions.
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,15 @@ import RouteData from '@jack-henry/web-component-router/lib/route-data.js';
* These should match to named path segments. Each camel case name
* is converted to a hyphenated name to be assigned to the element.
* @param {boolean=} requiresAuthentication (optional - defaults true)
* @param {function():Promise<undefined>=} beforeEnter Optionally allows you to dynamically import the component for a given route. The route-mixin.js will call your beforeEnter on routeEnter if the component does not exist in the dom.
*/
const routeData = new RouteData(
'Name of this route',
'tag-name',
'/path/:namedParameter',
['namedParameter'], // becomes attribute named-parameter="value"
true);
true,
() => import('../tag-name.js'));
```

It is recommended to use enums and module imports to define the paths
Expand Down Expand Up @@ -119,16 +121,19 @@ const routeConfig = {
tagName: 'APP-USER-PAGE',
path: '/users/:userId([0-9]{1,6})',
params: ['userId'],
beforeEnter: () => import('../app-user-page.js')
}, {
id: 'app-user-account',
tagName: 'APP-ACCOUNT-PAGE',
path: '/users/:userId([0-9]{1,6})/accounts/:accountId([0-9]{1,6})',
params: ['userId', 'accountId'],
beforeEnter: () => import('../app-account-page.js')
}, {
id: 'app-about',
tagName: 'APP-ABOUT',
path: '/about',
authenticated: false,
beforeEnter: () => import('../app-about.js')
}]
};
Expand Down
5 changes: 4 additions & 1 deletion lib/route-data.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ class RouteData {
* @param {!Array<string>=} namedParameters list in camelCase. Will be
* converted to a map of camelCase and hyphenated.
* @param {boolean=} requiresAuthentication
* @param {function():Promise<undefined>=} beforeEnter
*/
constructor(id, tagName, path, namedParameters, requiresAuthentication) {
constructor(id, tagName, path, namedParameters, requiresAuthentication, beforeEnter) {
namedParameters = namedParameters || [];
/** @type {!Object<string, string>} */
const params = {};
Expand All @@ -28,6 +29,8 @@ class RouteData {
/** @type {!Element|undefined} */
this.element = undefined;
this.requiresAuthentication = requiresAuthentication !== false;

this.beforeEnter = beforeEnter || (() => Promise.resolve());
}
}

Expand Down
4 changes: 1 addition & 3 deletions lib/routing-mixin.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ function routingMixin(Superclass) {
async routeEnter(currentNode, nextNodeIfExists, routeId, context) {
context.handled = true;
const currentElement = /** @type {!Element} */ (currentNode.getValue().element);

if (nextNodeIfExists) {
const nextNode = /** @type {!RouteTreeNode} */ (nextNodeIfExists);

Expand All @@ -37,13 +36,13 @@ function routingMixin(Superclass) {
const thisElem = /** @type {!Element} */ (/** @type {?} */ (this));
/** @type {Element} */
let nextElement = nextNodeData.element || thisElem.querySelector(nextNodeData.tagName.toLowerCase());

// Reuse the element if it already exists in the dom.
// Add a sanity check to make sure the element parent is what we expect
if (!nextElement || nextElement.parentNode !== currentElement) {
if (nextNodeData.tagName.indexOf('-') > 0) {
let Elem = customElements && customElements.get(nextNodeData.tagName.toLowerCase());
if (!Elem) {
await nextNodeData.beforeEnter();
// When code splitting, it's possible that the element created is not yet in the registry.
// Wait until it is before creating it
await customElements.whenDefined(nextNodeData.tagName.toLowerCase());
Expand Down Expand Up @@ -74,7 +73,6 @@ function routingMixin(Superclass) {
}
currentElement.appendChild(nextElement);
}

nextNode.getValue().element = /** @type {!Element} */ (nextElement);
} catch (e) {
// Internet Explorer can sometimes throw an exception when setting attributes immediately
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@jack-henry/web-component-router",
"version": "3.6.0",
"version": "3.7.0",
"description": "Web Components Router",
"main": "router.js",
"type": "module",
Expand Down
3 changes: 2 additions & 1 deletion router.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
* params: (Array<string>|undefined),
* authenticated: (boolean|undefined),
* subRoutes: (Array<RouteConfig>|undefined),
* beforeEnter: (function():Promise<undefined>|undefined)
* }} RouteConfig
*/
let RouteConfig;
Expand Down Expand Up @@ -84,7 +85,7 @@ class Router {
/** @param {!RouteConfig} routeConfig */
buildRouteTree(routeConfig) {
const authenticated = [true, false].includes(routeConfig.authenticated) ? routeConfig.authenticated : true;
const node = new RouteTreeNode(new RouteData(routeConfig.id, routeConfig.tagName, routeConfig.path, routeConfig.params || [], authenticated));
const node = new RouteTreeNode(new RouteData(routeConfig.id, routeConfig.tagName, routeConfig.path, routeConfig.params || [], authenticated, routeConfig.beforeEnter));
if (routeConfig.subRoutes) {
routeConfig.subRoutes.forEach(route => {
node.addChild(this.buildRouteTree(route));
Expand Down
2 changes: 2 additions & 0 deletions test/router-spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ describe('Router', () => {
path: '/users/:userId([0-9]{1,6})',
requiresAuthentication: true,
params: ['userId'],
beforeEnter: () => Promise.resolve(),
}, {
id: 'app-user-account',
tagName: 'APP-ACCOUNT-PAGE',
Expand All @@ -136,6 +137,7 @@ describe('Router', () => {
if (testSubRouteData[index].params) {
expect(Object.keys(data.attributes)).toEqual(testSubRouteData[index].params);
}
expect(data.beforeEnter).not.toBe(undefined);
['id', 'tagName', 'path', 'requiresAuthentication'].forEach((prop) => {
expect(data[prop]).toBe(testSubRouteData[index][prop]);
});
Expand Down
1 change: 1 addition & 0 deletions test/utils/test-route-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const testRouteConfig = {
tagName: 'APP-USER-PAGE',
path: '/users/:userId([0-9]{1,6})',
params: ['userId'],
beforeEnter: () => Promise.resolve(),
}, {
id: 'app-user-account',
tagName: 'APP-ACCOUNT-PAGE',
Expand Down

0 comments on commit 9a75bbb

Please sign in to comment.