3.3.0 • Published 7 years ago

sparouter v3.3.0

Weekly downloads
32
License
MIT
Repository
github
Last release
7 years ago

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 configDescription
modehash (by default) and html5 history.
scrollhandle navigation to fragment (true by default)
Route configDescription
paththe path pattern ("/posts" or "posts/:id" or "/posts/:id(a-z+)" for example)
nameroute name
actionan action
actionsan array of actions
dataextra data to pass
canActivateroute guards
canDeactivateroute guards
redirectToredirect to route url
childrennested 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
});
3.3.0

7 years ago

3.2.1

7 years ago

3.2.0

7 years ago

3.1.0

7 years ago

3.0.2

7 years ago

3.0.1

7 years ago

3.0.0

7 years ago

2.5.8

7 years ago

2.5.7

7 years ago

2.5.6

7 years ago

2.5.5

7 years ago

2.5.4

7 years ago

2.5.3

7 years ago

2.5.2

7 years ago

2.5.1

7 years ago

2.5.0

7 years ago

2.4.3

7 years ago

2.4.2

7 years ago

2.4.1

7 years ago

2.4.0

7 years ago

2.3.0

7 years ago

2.2.2

7 years ago

2.2.1

7 years ago

2.2.0

7 years ago

2.1.0

7 years ago

2.0.0

7 years ago