Skip to content

v-nvtsk/npm-package-routelib

Repository files navigation

Client side browser Router lib

Router supports routes of types:

  • strings
  • regular expressions
  • functions

Router supports asynchronous hooks:

  • onBeforeEnter: called before page loading
  • onEnter: called on page loading
  • onLeave: called on page leaving

Router supports hash API and History API. Can be chosen on router instance creation.

On init Router sets the listener on document click and check if target is anchor. If anchor href is relative and starts from "/" it will be processed as route link. Any other links will be opened in a new window.

Installing

npm install routelib

Once the package is installed, you can import the library using import or require approach:

import { Router } from "routelib";

Router API public methods

addRoute

Adds new route

  addRoute(routeConfig: RouteConfig): void {

Where RouteConfig is an object that describe route path and callbacks (hooks) to process on navigation to this route (or out of it: onLeave):

type RouteConfig = {
  path: string | RegExp | Function; // Path as string or RegExp for pattern matching
  onBeforeEnter?: (params?: HookParams) => Promise<void> | void;
  onEnter: (params?: HookParams) => Promise<void> | void;
  onLeave?: (params?: HookParams) => Promise<void> | void;
};

and HookParams is an object with data:

type HookParams = {
  [key: string]: string;
};

removeRoute

If you don't need route any more - it could be removed

  removeRoute(path: string):void

setNotFoundRoute

To handle route errors you can add handler for not found routes.

  setNotFoundRoute(routeConfig: Omit<RouteConfig, "path">): void;

To handle error 404 your server should redirect any call to index.html! In webpack you can achive this by adding parameter historyApiFallback

module.exports = (env) => ({
  ...
  devServer: {
    ...
    historyApiFallback: true,
  },

navigate

You can navigate to any route by calling navigate method. Optional parameter update allows only change href without render. It's more internal option.

  navigate(path: string, update = true): void;

Example

import { Router } from "../src/RouterLib";

// Create initial markup
const nav = document.createElement("nav");
nav.id = "nav";
nav.innerHTML = `
  <ul>
    <li><a href="/">Home</a></li>
    <li><a href="/about">About</a></li>
    <li><a href="/year">Year view</a></li>
    <li><a href="/month">Month view</a></li>
    <li><a href="/month?filter=true&month=1&text=task1&tag=tag1">Month with params view</a></li>
    <li><a href="/day">Day view</a></li>
    <li><a href="/list">List view</a></li>
    <li><a href="/function">Function view</a></li>
    <li><a href="/asd123">Regular expression</a></li>
    <li><a href="/any">Any unknown page</a></li>
    <li><a href="/removed">This path is added and removed</a></li>
    <li><a href="https://google.com">External link - google.com</a></li>

  </ul>
`;

// append body with element
document.body.append(nav);

// create element to which will print information
const info = document.createElement("div");
info.id = "info";
document.body.append(info);

// creating router with modern History API
const router = new Router("history");

// creating router with hash API
// const router = new Router("hash");


// helper with onEnter hook, that will output information about
// selected route
const callback = (path: string | RegExp | Function) => ({
  path,
  onEnter: () => {
    console.log("Entered", path);
    if (typeof path === "string") {
      document.title = path.slice(1);
      const el = document.createElement("div");
      el.innerHTML = path;
      info.append(el);
    }
  },
});

// add Root route
router.addRoute(callback("/"));

// add About route demonstrating onBeforeEnter, onEnter
// and onLeave hooks with timer API
router.addRoute({
  path: "/about",
  onBeforeEnter: async () => {
    console.log("Before entering about page - wait 3 seconds...");
    let el = document.createElement("div");
    el.innerHTML = "Before entering about page - wait for 3...";
    info.append(el);
    await new Promise((resolve) => {
      setTimeout(resolve, 1000);
    });
    el = document.createElement("div");
    el.innerHTML = "Before entering about page - wait for 2...";
    info.append(el);
    await new Promise((resolve) => {
      setTimeout(resolve, 1000);
    });
    el = document.createElement("div");
    el.innerHTML = "Before entering about page - wait for 1...";
    info.append(el);
    await new Promise((resolve) => {
      setTimeout(resolve, 1000);
    });
  },
  onEnter: () => {
    console.log("Entered about page");
    document.title = "About";
    const el = document.createElement("div");
    el.innerHTML = "About";
    info.append(el);
  },
  onLeave: async () => {
    console.log("Leaving about page");
    const el = document.createElement("div");
    el.innerHTML = "Leaving about page - wait 3 seconds...";
    info.append(el);
    await new Promise((resolve) => {
      setTimeout(resolve, 3000);
    });
  },
});

// add route supporting with regular expression
router.addRoute({
  path: /[\S]{1,3}123/,
  onEnter: () => {
    console.log("Entered RegExp page");
    info.innerHTML = "RegExp";
    document.title = info.innerHTML;
  },
});

// add route supporting function as route
router.addRoute({
  path: () => "/function",
  onEnter: () => {
    console.log("Entered /function page");
    info.innerHTML = "Function";
    document.title = info.innerHTML;
  },
});

// add route with helper `callback`
router.addRoute(callback("/year"));
router.addRoute(callback("/month"));
router.addRoute(callback("/day"));
router.addRoute(callback("/list"));
router.addRoute(callback("/removed"));
router.removeRoute("/removed");

// add default route for any not found page
// 404 page route
router.setNotFoundRoute({
  onEnter: () => {
    console.log("404 - Page not found");
    info.innerHTML = "404 - Page not found";
    document.title = "404 - Page not found";
  },
});

// start navigation from Root
router.navigate("/");