sparouter
TypeScript icon, indicating that this package has built-in type declarations

3.3.0 • Public • Published

Spa Router v3

A router developed with TypeScript with :

  • 2 modes : html5 history,hash
  • page animation/transition with css or js
  • active elements
  • route guards
  • child routes
  • actions

Build Status npm version

Installation

npm i sparouter -S

Workflow

With TypeScript / es6

We could use a starter kit.

With es5

Its possible. Reference the lib in the main page.

<body>
 <script src="node_modules/sparouter/dist/sparouter.js"></script> 
 <script src="src/app.js"></script> 
</body>
new SpaRouter.Router().map([
    { path: "/", action: function () { return SpaRouter.render({ selector: "#main", template: "<h1>Home</h1>" }); } },
    { path: "**", redirectTo: "/" }
]).run();

Router & route configs

Router config Description
mode hash (by default) and html5 history.
scroll handle navigation to fragment (true by default)
Route config Description
path the path pattern ("/posts" or "posts/:id" or "/posts/:id([a-z]+)" for example)
name route name
action an action
actions an array of actions
data extra data to pass
canActivate route guards
canDeactivate route guards
redirectTo redirect to route url
children nested routes
import  { Router } from "sparouter";
 
const routes = [
    { path: "/", action: () => document.querySelector("#main").innerHTML = "<h1>Home</h1>" },
    { path: "/posts", action: () => render({ selector: "#main", templateUrl: "views/posts.html" }) },
    { path: "/posts/:id", canActivate: [MyGuard], action: ({route, router}) => console.log("Activate post details") },
    { path: "**", redirectTo: "/" },
];
 
new Router({
    mode: "html5"
}).map(routes).run((route) => {
    // on route change success
}, (err) => {
    //  route change error ("aborted" with a guard or "notfound" if no matched route found)
});

With html5 history mode (uris without '#'), the server have to redirect to index page.

The base tag with html5 history mode allow to set the base path. Examples:

<base href="/"/>

or

<base href="http://mysite.com/blog/"/>

Param regex (number by default)

Example:

const routes =[
    { path: "/posts/:id([a-z]+)", /* etc. */ }
];

Named routes:

const routes = [
    { name: "home", path: "/", /* etc. */ },
    { name: "posts", path: "/posts", /* etc. */ }
];

Route with actions (array of functions)

const routes = [
    {
        path: "/",
        actions: [
            () => document.querySelector("#main").innerHTML = "<h1>Home</h1>",
            ({ route, router }) => console.log("Activate home", route, router),
            /* other actions */
        ]
    }
];

Its possible to pass an "action result" to the next action

const routes = [
   {
       path: "/", actions: [
           () => { return ["a", "b", "c"]; },
           ({ result, router }) => { console.log(result); }
       ]
   }
];

... Or with a promise

const routes = [
   {
       path: "/", actions: [
           () => {
               return new Promise((resolve) => {
                   resolve(["a", "b", "c"]);
               });
           },
           ({ result, router }) => { console.log(result); }
       ]
   }
];

children

const routes = [
    { path: "/", templateUrl: "src/views/home.html" },
    {
        path: "posts", 
        children: [ 
            { path: "", action: () => { /* do something */ } },
            { path: ":id", actions: [ /* do things */ ]  }
        ]
    }
];

Links

With hash mode

<a href="#/">Home</a>
<a href="#/posts">Posts</a>
<a href="#/posts/10">With parameter</a>
<a href="#/posts/10?q=news#section1">Query and fragment</a>

With to attribute: the best way to switch easilly between "hash" and "html5 history"

<a to="/">Home</a>
<a to="/posts">Posts</a>
<a to="/posts/10">With parameter</a>
<a to="/posts/10?q=news#section1">Query and fragment</a>

Active attributes

  • active-class the css class to add if active
<a href="/posts" active-class="active">Posts</a>
.active {
   color:red
 }
  • active-path allow to set a regex pattern or to add on any element ("li" for example)
<li active-path="/posts" active-class="active"></li>
  • active-exact the css class is only added if path + query + fragment equal to link href or active-path
<a href="/posts/10?q=abc#section1" active-class="active" active-exact="true">Details</a>
<!-- with active-path -->
<a href="/posts/10?q=abc#section1" active-path="/c/([a-z]+)\\?q=10#section1" active-class="active" active-exact="true">Details</a>

Navigate programmatically

Navigate by route name

router.navigateTo("home");
// with parameter
router.navigateTo("post-detail",{ id: 10});
// with query and fragment
router.navigateTo("post-detail",{ id: 10},{ q: "news" },"section1");

Navigate by url

router.navigateToUrl("/");
// with parameter
router.navigateToUrl("/posts/10");
// with query and fragment
router.navigateToUrl("/posts/10?q=news#section1");

Go back

router.goBack();

Go forward

router.goForward();

render function

Allow to render content in an HTMLElement, and create an instance of a vm and pass args.

import { render } from "sparouter";
 
class PostDetail {
    onActivate(route, router,scope) {
       // route with params, query, fragment and data
    }
}
 
const routes = [
    { path: "/posts", action: () => render({ selector: "#main", templateUrl: "views/posts.html" }) },
    { path: "/posts/:id", action: ({ route, router }) => render({ selector: "#main", templateUrl: "views/post-detail.html", vm: PostDetail, args: [route, router] }) }
];

Async await or promises

Allow to wait the end of the action before reach the next

Example with async await

function doSomething() {
    return new Promise((resolve) => {
        setTimeout(function () {
            console.log("Completed");
            resolve();
        }, 5000);
    });
}
 
const routes = [{ path: "/", action: async() => {
    await doSomething();
}}];

Example with promise

const routes = [{
    path: "/", action: () => {
        return new Promise((resolve) => {
            setTimeout(function () {
                console.log("Completed");
                resolve();
            }, 5000);
        });
    }
}];

Page transition

with navigate function

Animation "leave" and "enter" (could be played simultaneously)

Example simple , a slide in / slide out

const routes = [
    { path: "/", action: () => navigate({ selector: "#main", template: "<h1>Home</h1>", enter: "slideInRight", leave: "slideOutLeft" }) }
];

Other example "Shuffle" on the container and

const routes = [
    { path: "/", action: () => navigate({ selector: "#main", template: "<h1>Home</h1>", enter: "navInPrev", leave: "navOutPrev", simultaneous: true }) }
];

Before each and after each

Usefull for page animations with javaScript (SVG for example)

var router = new SpaRouter.Router().map(routes).beforeEach((next) => {
    next();
}).afterEach(() => {
    
}).run();

Route guards

class PostDetail {
    checkDeactivate() {
        return confirm("Leave this page?");
    }
}
 
class MyGuard implements CanActivate, CanDeactivate {
     canActivate(route, next) {
        let result = confirm("Navigate?");
        next(result);
    }
 
    canDeactivate(activeVms, route, next) {
        let vm = activeVms["PostDetail"];
        let result = vm && vm.checkDeactivate ? vm.checkDeactivate() : true;
        next(result);
    }
}

Example route with guard:

const routes = [
    { path:"/posts/:id", canActivate: [MyGuard], canDeactivate: [MyGuard], /* etc. */ }
]);

Or register with injector

injector.registerSecure("MyGuard",MyGuard);
 
const routes = [
    { path:"/posts/:id", canActivate: ["MyGuard"], /* etc. */ }
]);

injector

Allow to inject services

Example

Create and register a service

function MyService() {
    this.getAll = function () {
        // return some data
    }
}
injector.register("MyService", MyService);

Inject the service

function MyVM(myService) { }
injector.register("MyVM", MyVM, ["MyService"]);

Register a secure service (service is not returned with getInstance/ getNewInstance and cannot be removed)

injector.registerSecure("MySecureService", MySecureService);

Chaining registrations

injector
    .register("MyService1", MyService1)
    .register("MyService2", MyService2);

Get an instance (create or get a cached instance)

let instance = injector.getInstance("MyService");

Get a new instance

let instance = injector.getNewInstance("MyService");

Invoke a function with Injector

injector.invoke(myFunc);

View usefull functions

Allow to select and animate HTML elements.

import { qs, qsa } from "sparouter";
 
qs(".box").changeContent("<h1>New content</h1>");
 
qs(".box").animate("fadeIn",() => {
    // completed
});

Readme

Keywords

Package Sidebar

Install

npm i sparouter

Weekly Downloads

58

Version

3.3.0

License

MIT

Last publish

Collaborators

  • romagny13