This is the second post in the series of building a Router component in Glimmer.js. In thefirst post we built our Router component from scratch for handling anchor tag clicks, history state changes and so on. In this post we are going to make use of a client-side routing library calledpage.js
for all those boilerplate code.

Building a Router component for Glimmer.js
Rajasegar Chandran ・ Dec 23 '20
About page.js
page is a tiny client-side router inspired by theExpress router. It has got a lot of features like 404 support, plugin integrations, using history state to cache data and more.
App.js
This is how our template markup is going to look like for the mainApp
component in our project. As you can see, we have omitted all the custom componets like<Link/>
and<Route/>
. Because all of them seems to be irrelevant here since we are going to use thepage
library, it can make use of the standard anchor tags in the browser and the route mapping will be done in pure javascript with thepage()
functions.
<nav><ul><li><ahref="/">Home</a></li><li><ahref="/about">About</a></li><li><ahref="/contact">Contact</a></li></ul></nav><Router/>
Router Component
Our Router component will become slim here. Because we are removing theLink
and theRoute
components since their functionality is taken care by page.js. Since page.js is going to take care of handling all the anchor click events we don't need a custom component to render our anchor tags. And the router registry which we created in our first post is also not required becuase page.js has its own way of keeping the route mappings.
Here we are using a modifier calledroutes
which is a function from a file calledroutes.js
where we will delegate all the routing logic to the page.js.
importroutesfrom'./routes.js';import{createTemplate,setComponentTemplate,templateOnlyComponent,}from'@glimmer/core';constRouter=setComponentTemplate(createTemplate({routes},` <div {{ routes }}></div> `),templateOnlyComponent())export{Router};
Let's take a look at ourroutes.js
file. First we are importingrenderComponent
function from@glimmer/core
library. Then we are importing thepage
library here with the default import. The main function takes anelement
parameter, which is nothing but a reference to the actual DOM element sent by our Glimmer engine when the element is inserted in to the DOM. This acts as our mount point for rendering the various page components into our DOM when a particular route is visited.
routes.js
import{renderComponent}from'@glimmer/core';importpagefrom'page';exportdefaultfunction(element){}
This is how we can make use ofpage
library to implement our routing. These are some example patterns from their project readme page.
page('/',index)page('/user/:user',show)page('/user/:user/edit',edit)page('/user/:user/album',album)page('/user/:user/album/sort',sort)page('*',notfound)page()
As you can see thepage()
function take two parameters, one the URL pattern with all the parent and child routes, and the dynamic segment placeholders. The second parameter is a callback function which handles the routing logic when the particular URL is visited.
Next we are going to use thepage
function to render our components based on the route. Let's focus on the home page route which takes an URL like/
. For this route we are going to render a component calledHome
from a folder calledpages
. This is just for convention, to keep things organized like all the page level components are created in thepages
folder.
...exportdefaultfunction(element){page('/',()=>{import('./pages/Home.js').then(component=>{element.innerHTML='';renderComponent(component.default,element);});}}
Injecting services
We can also inject services into our components while rendering them. TherenderComponent
function takes an options object as the second parameter in which you can inject services and set the arguments for the component. Let's inject a service calledLocaleService
and initialize it with the language codeen_US
into ourHome
page component.
...exportdefaultfunction(element){page('/',showPageLoad,()=>{import('./pages/Home.js').then(component=>{element.innerHTML='';renderComponent(component.default,{element:element,owner:{services:{locale:newLocaleService('en_US'),},}});});});}
Page loading indicators
It might take some time to fetch our component code and render them on to the DOM. So we want to show some loading status to our users while the components are being fetched.
With the current setup how we are going to achieve that. Thepage
library takes any number of callbacks for route handling and it will execute them one by one in the order
we are specifying. So we will add one more callback function calledpageLoad
before our anonymous rendering callback in the routing logic.
functionshowPageLoad(ctx,next){element.innerHTML='<h1>Loading page...</h1>';next();}
In thisshowPageLoad
callback we are doing two things. First, we will set our loading indicator html markup in our mount node in the DOM calledelement
. Second we
are using the thenext
callback function given by page.js to move to the next callback in our routing logic sequence. This will allow us to do different things
even before mounting our page level components on to the DOM.
page('/',showPageLoad,()=>{import('./pages/Home.js').then(component=>{element.innerHTML='';renderComponent(component.default,{element:element,owner:{services:{locale:newLocaleService('en_US'),},}});});});
Finally we have to call thepage()
method to start routing and listening for anchor clicks and navigation events like history state changes in the browser.
exportdefaultfunction(element){...page();}
This is how our finalroutes.js
file is going to look like for handling various routes like/about
,/contact
, etc., You can see we are using theshowPageLoad
callback prior to rendering the respective page components.
import{renderComponent}from'@glimmer/core';importpagefrom'page';exportdefaultfunction(element){functionshowPageLoad(ctx,next){element.innerHTML='Loading page...';next();}page('/',showPageLoad,()=>{import('./pages/Home.js').then(component=>{element.innerHTML='';renderComponent(component.default,{element:element,owner:{services:{locale:newLocaleService('en_US'),},}});});});page('/about',showPageLoad,()=>{import('./pages/About.js').then(component=>{element.innerHTML='';renderComponent(component.default,element);});});page('/contact',showPageLoad,()=>{import('./pages/Contact.js').then(component=>{element.innerHTML='';renderComponent(component.default,element);});});page();}
Source code
You can see the full source code for this post in theglimmer-snowpack repository, which is actually acreate-snowpack-app template to build Glimmer apps withSnowpack. You can use this template like below:
npx create-snowpack-app my-glimmer-app --template glimmer-snowpack
Please let me know any feedback or queries in the comments section. In the next post in this series, we are going to make use of another client side routing library to build ourRouter
component.
Top comments(0)
For further actions, you may consider blocking this person and/orreporting abuse