Building a Glimmer Router with Navigo

Rajasegar Chandran - Jan 7 '21 - - Dev Community

This is the third post in the series of building a Router component in Glimmer.js. In the second post we built our Router component using page.js for handling anchor tag clicks, history state changes and so on. In this post we are going to make use of a library called navigo for all those capabilities.

Navigo

Navigo is a simple dependency-free minimalistic JavaScript router with a fallback for older browsers. It is based on History API so it does update the URL of the page. It has got a simple mapping of route to a function call, parameterized routes, programmatic navigation between routes and lifecycle hooks such as before, after, leave, already. It can be easily integrated with HTML links via data-navigo attribute.

You can install it with:

yarn add navigo -S
Enter fullscreen mode Exit fullscreen mode

Navigo is created by Krasimir Tsonev based on this blog post by him called A modern Javascript router in 100 lines.

The api of Navigo is very simple. First you have to create a new instance of Navigo,

const router = new Navigo('/');
Enter fullscreen mode Exit fullscreen mode

And then using that instance, you have to define your route mappings with the on function of the router instance. The on function takes two parameters, one is the url and the next one is the handler function for that particular url.

router.on('/products/list', function() {
  // do something
});
Enter fullscreen mode Exit fullscreen mode

And finally, you have to trigger the resolving logic.

router.resolve()
Enter fullscreen mode Exit fullscreen mode

App.js

We will be modifying our App component from the previous post. The markup for our App.js component will look something like this. As you see we are using two components here, LinkTo and Router. And our Router component is where the routing logic is placed.

<nav>
  <ul>
    <li><LinkTo @route="/">Home</LinkTo></li>
    <li><LinkTo @route="about">About</LinkTo></li>
    <li><LinkTo @route="contact">Contact</LinkTo></li>
  </ul>
</nav>
<main>
  <Router/>
</main>
Enter fullscreen mode Exit fullscreen mode

LinkTo component

LinkTo is to render the anchor tags with special attributes for Navigo called data-navigo so that Navigo can pick up these elements and listen for their click events. The markup for LinkTo will be like:

<a href={{@route}} data-navigo>{{yield}}</a>
Enter fullscreen mode Exit fullscreen mode

Router component

This is just a placeholder component for rendering the page level components when the url is matched. All the routing logic is done through a modifier called startNavigo.

<div id="glimmer-router-outlet" {{startNavigo}}></div>
Enter fullscreen mode Exit fullscreen mode

startNavigo modifier

The startNavigo modifier function is where we will instantiate the Navigo library and invoke it with the route mappings, the url and the handler functions from a separate file called routes.js.

function startNavigo(element) {

  const router = new Navigo("/");
  const navigoRoutes = routes(element);

  router.on(navigoRoutes).resolve();

  return () => {
    router.destroy();
  }

}
Enter fullscreen mode Exit fullscreen mode

routes.js

The routes.js contains the route mappings for the various routes within the app. It will export a default function which takes only one parameter the element, which is passed on by the modifier function startNavigo in the previous section. And it returns an object of route mappings with the url patterns and the handler functions.

Inside each handler function, we will be resetting the element DOM content and then we will be using the renderComponent function from @glimmer/core to render the appropriate component. All the components are stored in a special folder called pages in the project in a conventional fashion.

import { renderComponent } from '@glimmer/core';

import Home from './pages/Home.js';
import About from './pages/About.js';
import Contact from './pages/Contact.js';

export default function(element) {
  return {
    'about': () => {
      element.innerHTML = '';
      renderComponent(About, element);
    }  ,
    'contact': () => {
      element.innerHTML = '';
      renderComponent(Contact, element);
    },
    '*': () => {
      element.innerHTML = '';
      renderComponent(Home, element);
    }
  };
}
Enter fullscreen mode Exit fullscreen mode

Source Code

The source code for this post is available in this Github repository.

References:

Please let me know for any queries/feedbacks in the comments section.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .