Router

Before Semicolon offers a routing package built with WebComponent which is built on top of Markup.

<nav>
    <page-link path="/">Home</page-link>
    <page-link path="/contact">Contact</page-link>
</nav>

<page-route path="/">
    Home content
</page-route>

<page-route path="/contact">
    Contact content
</page-route>

<page-route path="/404">
    404 - Page not found!
</page-route>

<page-redirect to="/404"></page-redirect>

Examples

Installation

via npm:

npm install @beforesemicolon/router

via yarn:

yarn add @beforesemicolon/router

via cdn:

<!-- required WebComponent Markup to be present -->
<script src="https://unpkg.com/@beforesemicolon/web-component/dist/client.js"></script>


<!-- use the latest version -->
<script src="https://unpkg.com/@beforesemicolon/router/dist/client.js"></script>

<!-- use a specific version -->
<script src="https://unpkg.com/@beforesemicolon/router@0.1.0/dist/client.js"></script>

<!-- link you app script after -->
<script>
    const { ... } = BFS.ROUTER
</script>

Components

Page route

You can specify the title and the body content to conditionally render.

<page-route path="/" title="Welcome">
  Home content
</page-route>

Lazy load html content with fallback content and loading indicator.

<page-route path="/contact" src="/contact.html">
  <div slot="loading">Loading...</div>
  <div slot="fallback">
    Oops - Failed to load content
  </div>
</page-route>

Import JavaScript file which must default export:

  • Text;
  • HTML string;
  • DOM Node;
  • Markup template;
  • Any object with a "render" method that takes an element to render at;
  • Function that returns any of the above;
<page-route 
    path="/greeting" 
    src="./greeting.page.js" 
></page-route>
// greeting.page.js
const { html } = BFS.MARKUP

export default () => {
  return html`<p>Hello World</p>`
}

Route nesting by specifying the exact attribute with false value.

<page-route path="/todos" exact="false">
    <!-- child page route already knows its inside a page-route 
       so its parent path already prefixes its own which means
       bellow page-route path is actually "/todos/pending" -->
    <page-route path="/pending">
        ...
    </page-route>
</page-route>

Page route query

The page-route-query work exactly like page-route but reacts to the search query of the url instead. It takes a key and value attributes instead of a path.

<page-route-query key="tab" value="sample">
  sample tab content
</page-route-query>

Use the default attribute to tell it to render content even if the key is not present.

<page-route-query key="tab" value="sample" default="true">
    sample tab content
</page-route-query>

A link that lets you navigate to any page. Works similar to goToPage and takes similar options.

<page-link 
  path="/"
  title="Welcome"
  data='{"sample": "value"}'
>
  Home Info
</page-link>

Paths can also contain search query in the path.

<page-link path="/router/index.html?tab=sample">sample tab</page-link>

Or specify it separately.

<page-link path="/sample" search="tab=info">new tab</page-link>

You can choose to keep current search query and only add your specified search.

<page-link path="/sample" search="tab=info" keep-current-search="true">new tab</page-link>

Similar to page-route-query default attribute, you can mark the specified search as default to put the link in an active state for styling purpose.

<page-link path="/todos" search="tab=pending" default="true">Pending Todos</page-link>
<page-link path="/todos" search="tab=in-progress">In Progress Todos</page-link>
<page-link path="/todos" search="tab=completed">Completed Todos</page-link>

The link is context aware and leaving out the path attribute defaults to the closest page-route path or /. You can also refer to the closed page route path with the $ prefix.

<page-route path="/todos">
    <page-link search="tab=pending" default="true">Pending Todos</page-link>
    <page-link search="tab=in-progress">In Progress Todos</page-link>
    <page-link search="tab=completed">Completed Todos</page-link>
    
    <page-link path="$/create">new tab</page-link>
</page-route>

Styling the page-link is straight forward allowing you to even target link states.

/* the actual page-link tag */
page-link {
    ...
}

page-link.active {
    ...
}

/* the anchor tag inside */
page-link::part(anchor) {
    text-decoration: none;
    color: #444;
    padding: 10px;
    border-bottom: 2px solid transparent;
}

page-link::part(anchor active) {
    background: #b4fff8;
    border-color: #222;
    color: #000;
}

page-link::part(anchor):visited { ... }
page-link::part(anchor):active { ... }
page-link::part(anchor):hover { ... }

Page redirect

The page-redirect lets you automatically redirect to a path if not a known one. You should place it after all page-route rendered on the page.

<page-route path="/" src="./index.html"></page-route>
<page-route path="/contact" src="./contact.html"></page-route>
<page-route path="/about" src="./about.html"></page-route>
<page-route path="/404"></page-route>
<!-- render it after all page-routes-->
<page-redirect to="/404"></page-redirect>

When placed inside a page-route, it will redirect whenever any unknown route starting with the parent page-route is detected.

<page-route path="/project">
    ...
    <page-redirect to="/project/not-found"></page-redirect>
</page-route>

Sometimes you want to always redirect to a child route when a parent path is matched and for that you can specify the redirect type to be always.

<page-route path="/todos" exact="false">
    Todos:
    <page-route path="/pending">pending todos</page-route>
    <page-route path="/in-progress">in progress todos</page-route>
    <page-route path="/completed">completed todos</page-route>
    <page-redirect to="/todos/pending" type="always"></page-redirect>
</page-route>
<page-route path="/404"></page-route>

<page-redirect to="/404"></page-redirect>
<page-redirect to="/todos" type="always"></page-redirect>

Additional APIs

You have access to internal APIs that you can use in your JavaScript code to perform any similar actions.

goToPage

Takes you to a new page pathname. It takes the path name, an optional data and a page title.

goToPage('/sample')
goToPage('/test', {sample: "value"}, 'test page')

replacePage

Replaces the current page pathname. It takes the path name, an optional data and a page title.

replacePage('/new', {data: 3000}, 'new page')

nextPage

Takes you to the next page.

nextPage()

previousPage

Takes you to the previous page.

previousPage()

onPageChange

A listener for page changes. Takes a callback function that its called with the path name, a search query object literal, and any data set for the page.

onPageChange((pathName, searchQuery, data) => {
	...
})

getSearchQuery

Returns a object literal representation of the search query. Parses any value including JSON strings.

getSearchQuery();

updateSearchQuery

Takes a object literal and updates the search query.

updateSearchQuery({
  date: "2020-01-01",
  sample: 30
})

getPageData

getPageData();