Home

Awesome

AWC Router

Build Status

Features

Examples

Examples and further documentation can be found in the examples folder and are hosted here

To run the examples locally run:

node exampleServer.js

Then enter this url in the browser:

http://localhost:3000/a-wc-router/examples/router/

Install

npm i a-wc-router

Usage

With HTML

<!DOCTYPE html>
<html>
    <head>
        <base href="/app/">
        <script type="module" src="/node_modules/a-wc-router/build/es6-bundled/src/router.js"></script>
        OR use a CDN
        <script type="module" src="https://unpkg.com/a-wc-router/src/router.js"></script>
        <script type="module" src="https://cdn.jsdelivr.net/npm/a-wc-router/src/router.js"></script>
        
        <script type="module" src="/src/my-page1.js"></script>
    </head>
    <body>
        <a is="router-link" href="/app/page1">Page 1</a>
        <a is="router-link" href="/app/page2">Page 2</a>
        <a-router  style="display: block;">
            <a-outlet>This content never shows because of the last catch all route</a-outlet>
            <a-route path="/page1" element="my-page1"></a-route>
            <a-route path="/page2" element="my-page2" import="/src/my-page2.js"></a-route>
            <a-route path="*"><template>Page not found</template></a-route>
        </a-router>
    </body>
</html>

With Custom Element

import '../node_modules/a-wc-router/build/es6-bundled/src/router.js';
// Or use a CDN
import 'https://unpkg.com/a-wc-router/src/router.js';
import 'https://cdn.jsdelivr.net/npm/a-wc-router/src/router.js';

import './my-page1.js';

class MyApp extends HTMLElement {
    connectedCallback() {
        if (this.isConnected) {
            this.innerHTML =   `
                <a is="router-link" href="/app/page1">Page 1</a>
                <a is="router-link" href="/app/page2">Page 2</a>
                <a-router  style="display: block;">
                    <a-outlet>This content never shows because of the last catch all route</a-outlet>
                    <a-route path="/page1" element="my-page1"></a-route>
                    <a-route path="/page2" element="my-page2" import="/src/my-page2.js"></a-route>
                    <a-route path="*"><template>Page not found</template></a-route>
                </a-router>
            `;
        }
    }
}

customElements.define('my-app', MyApp);

Routing

Routing using a router, outlet, routes and HTML anchors

<a-router>
    <a-outlet>Please click a link</a-outlet>
    <a-route path="/user" import="./userBundle.js" element="user-main"></a-route>
    <a-route path="/item" import="./itemsBundle.js" element="item-main"></a-route>
    <a-route path="/template"><template>Hello Template</template></a-route>
    <a-route path="*"><template></template></a-route>
<a-router>
....
<a href='user'>click for user-main custom element</a>
<a href='item'>click for item-main custom element</a>

Passing data to routes

<a-route path="/user1/:requiredParam" element="user-main"></a-route>
<a-route path="/user2/:optionalParam?" element="user-main"></a-route>
<a-route path="/user2/:optionalParam?defaultValue" element="user-main"></a-route>
<a-route path="/user3/:atLeastOneParam+" element="user-main"></a-route>
<a-route path="/user4/:anyNumOfParam*" element="user-main"></a-route>
<a-route path="/user5/:firstParam/:secondParam" element="user-main"></a-route>
<!-- use a '.' as first char to set data as a property of the element instead of  an attribute -->
<a-route path="/user5/:.dataAsPropertyInsteadOfAttribute" element="user-main"></a-route>

<!-- constant params via attributes -->
<a-route path="/user6" data-firstParam="valueA" data-secondParam="valueB" element="user-main">
    <!-- This create <user-main firstParam="valueA" secondParam="valueB"> but will be overridden by values in the route -->
</a-route>
<script>
    // Constant params via set properties
    const myRoute = document.querySelector('a-route');
    // Set constant param programmatically
    myRoute.firstParam = 'valueA';
    myRoute.secondParam = new Date();
    // This will set the firstParam and secondParam as properties on the route content
</script>
<a href="user1/12">click for user with required param</a>
<a href="user2">click for user with optional param</a>
<a href="user3/12/tom">click for user with at least one param</a>
<a href="user4/12/tom">click for user any number of params</a>
<a href="user5/12/tom">click for user with two named params</a>

Code splitting and eager loading modules for routes

<a-route path="/user" import="/path-to/user-main.js" element="user-main"></a-route>

Code splitting and lazy loading modules for routes

<a-route path="/user" import="/path-to/user-main.js" lazy-load element="user-main"></a-route>

Nested Routing

<a-router>
    <a-outlet></a-outlet>
    <a-route path="/user" import="./userBundle.js" element="user-main"></a-route>
    <a-route path="/item" import="./itemsBundle.js" element="item-main"></a-route>
    <a-route path="*"><template></template></a-route>
<a-router>

// Code snippet from user-main with nested routing
connectedCallback() {
    if (this.connected && !this.innerHTML) {
        this.innerHTML = `
            <a-router>
                <a-outlet></a-outlet>
                <a-route path="/details" element="user-details"></a-route>
                <a-route path="/edit" element="user-edit"></a-route>
            </a-route>
            `;
    }
}
....
<a href='/user/details'>Navigate to the nested route</a>

Base URL

Routing only takes place if a url also matches the document.baseURI.

<base href="/MyAppRoot/">

<a href='/user'>Wont route</a>
<a href='/MyAppRoot/user'>Will route</a>

Named outlets (no router or routes required)

Routing using named outlets and HTML anchors

<a-outlet name="main">Please click a link</a-outlet>
....
<a href="/(main:user-main)">Assign <user-main> element to outlet main</a>
<a href="/(main:item-main)">Assign <item-main> element to outlet</a>

Passing data to named outlets

<a-outlet name="main">Please click a link</a-outlet>
....
<a href="/(main:user-main:userId=2&userName=tom)">Assign <user-main userId="2" userName="tom"> to outlet</a>

Code splitting and eager loading modules for named outlets

<a-outlet name="main">Please click a link</a-outlet>
....
<a class="item" is="router-link" href="(main:user-main(/path-to/user-main.js))">Load <user-main> from /path-to/user-main.js</a>
<a class="item" is="router-link" href="(main:/path-to/user-main)">Load <user-main> from /path-to/user-main.js</a>

General to Routing and Named Outlets

Styling link matching the active routes

<style>
    a.active {
        color: red;
    }
</style>

<a class="item" href="/user/123">Regular anchor - will route but wont get active status styling</a>
<a is="router-link" class="item" href="/user/123">Router link - will route and get active status styling</a>

Navigating using HTML anchors

<a class="item" href="/user/123">Regular anchor - will route but wont get active status styling</a>
<a is="router-link" class="item" href="/user/123">outer link - will route and get active status styling</a>

Navigating using events

window.dispatchEvent(
    new CustomEvent(
        'navigate', {
            detail: {
                href: '/(user/123' }}));

Navigating using RouterElement

RouterElement.navigate('myUrl');
RouterElement.navigate('/user/123');

Guards

window.addEventListener('onRouteLeave', guard);

guard(event) {
    if (document.getElementById('guard').checked) {
        // preventDefault to prevent the navigation
        event.preventDefault();
    }
}

Lifecycle Events

onRouterAdded

onRouteMatch

onRouteLeave

onRouteNotHandled

onRouteCancelled

onLinkActiveStatusUpdated

onOutletUpdated

Testing

To run tests:

npm test