# sparouter

> A router developed with TypeScript

Latest version **3.3.0** (published 2017-02-02) · MIT license · 0 weekly downloads

## Install

```sh
npm install sparouter
pnpm add sparouter
yarn add sparouter
bun add sparouter
```

## Health

**Score 25/100 (F)** — status: abandoned.

Positive: has types; no vulnerabilities; high quality score.

Warnings: low downloads; no esm support.

Negative: abandoned; low maintenance score.

## Facts

| | |
|---|---|
| Version | 3.3.0 |
| Published | 2017-02-02 |
| First published | 2017-01-01 |
| Weekly downloads | 0 |
| License | MIT |
| TypeScript types | bundled |
| Module format | CommonJS |
| Dependencies | 0 |
| Known vulnerabilities | 0 |
| Install scripts | no |
| Author | romagny13 |
| Maintainers | romagny13 |
| Keywords | spa, sparouter, router |

## Links

- npm: https://www.npmjs.com/package/sparouter
- Repository: https://github.com/romagny13/sparouter
- Homepage: https://github.com/romagny13/sparouter#readme
- Issues: https://github.com/romagny13/sparouter/issues
- npm.io page: https://npm.io/package/sparouter

## Alternatives

- [express-promise-router](https://npm.io/package/express-promise-router.md) — 736.1K weekly downloads
- [next-usequerystate](https://npm.io/package/next-usequerystate.md) — 29.8K weekly downloads
- [@bitkyc08/opencodex](https://npm.io/package/@bitkyc08/opencodex.md) — 4.6K weekly downloads
- [lynkr](https://npm.io/package/lynkr.md) — 575 weekly downloads
- [baremetal.js](https://npm.io/package/baremetal.js.md) — 42 weekly downloads

## Recent versions

- 3.3.0 (latest) — 2017-02-02
- 3.2.1 — 2017-02-02
- 3.2.0 — 2017-02-01
- 3.1.0 — 2017-01-31
- 3.0.2 — 2017-01-26
- 3.0.1 — 2017-01-26
- 3.0.0 — 2017-01-26
- 2.5.8 — 2017-01-23
- 2.5.7 — 2017-01-23
- 2.5.6 — 2017-01-22
- 2.5.5 — 2017-01-22
- 2.5.4 — 2017-01-21
- 2.5.3 — 2017-01-21
- 2.5.2 — 2017-01-18
- 2.5.1 — 2017-01-17
- … 11 more at https://npm.io/package/sparouter/versions

## README

# Spa Router v3

A router developed with <strong>TypeScript</strong> with :
- <strong>2 modes</strong> : <strong>html5 history</strong>,<strong>hash</strong>
- <strong>page animation/transition with css or js</strong>
- <strong>active</strong> elements
- <strong>route guards</strong>
- <strong>child routes</strong>
- <strong>actions</strong>


[![Build Status](https://travis-ci.org/romagny13/sparouter.svg?branch=master)](https://travis-ci.org/romagny13/sparouter) [![npm version](https://badge.fury.io/js/sparouter.svg)](https://badge.fury.io/js/sparouter) 

## Installation

```
npm i sparouter -S
```

## Workflow

### With TypeScript / es6

We could use a <a href="https://github.com/romagny13/starter-project-typescript">starter kit</a>.

### With es5

Its possible. Reference the lib in the main page.

```html
<body>
 <script src="node_modules/sparouter/dist/sparouter.js"></script>
 <script src="src/app.js"></script>
</body>
```

```js
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 | <strong>hash</strong> (by default) and <strong>html5</strong> 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

<img src="http://res.cloudinary.com/romagny13/image/upload/v1483654173/captureurl_ejcmab.png" />

```js
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 <strong>html5</strong> history mode (uris without '#'), the server have to redirect to index page.

The <strong>base tag with html5 history mode</strong> allow to set the base path. Examples:
```html
<base href="/"/>
```
or
```html
<base href="http://mysite.com/blog/"/>
```

## Param regex (number by default)

Example:

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

## Named routes:

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

## Route with actions (array of functions)

```js
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

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

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

### children

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

### Links

With <strong>hash mode</strong>
```html
<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 <strong>to attribute</strong>: the best way to switch easilly between "hash" and "html5 history"
```html
<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>
```

<strong>Active</strong> attributes
- <strong>active-class</strong> the <strong>css class</strong> to add if active
```html
<a href="/posts" active-class="active">Posts</a>
```
```css
.active {
   color:red
 }
````
- <strong>active-path</strong> allow to set a <strong>regex</strong> pattern or to add on any element ("li" for example)
```html
<li active-path="/posts" active-class="active"></li>
```
- <strong>active-exact</strong> the css class is only added if path + query + fragment equal to link href or active-path
```html
<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 <strong>by route name</strong>
```js
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 <strong>by url</strong>
```js
router.navigateToUrl("/");
// with parameter
router.navigateToUrl("/posts/10");
// with query and fragment
router.navigateToUrl("/posts/10?q=news#section1");
```

Go back
```js
router.goBack();
```

Go forward
```js
router.goForward();
```

## render function

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

```js
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

```js
function doSomething() {
    return new Promise((resolve) => {
        setTimeout(function () {
            console.log("Completed");
            resolve();
        }, 5000);
    });
}

const routes = [{ path: "/", action: async() => {
    await doSomething();
}}];
```

Example with promise

```js
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

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

Other example "Shuffle" on the container and
```js
const routes = [
    { path: "/", action: () => navigate({ selector: "#main", template: "<h1>Home</h1>", enter: "navInPrev", leave: "navOutPrev", simultaneous: true }) }
];
```

<img src="http://res.cloudinary.com/romagny13/image/upload/v1485903103/anim_perzdm.png" />


## Before each and after each 

Usefull for page animations with javaScript (SVG for example)

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

## Route guards

```js
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:
```js
const routes = [
    { path:"/posts/:id", canActivate: [MyGuard], canDeactivate: [MyGuard], /* etc. */ }
]);
```

Or register with injector
```js
injector.registerSecure("MyGuard",MyGuard);

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

### injector

Allow to inject services

Example
 
 Create and <strong>register a service</strong>

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

<strong>Inject</strong> the service

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

Register a <strong>secure service</strong> (service is not returned with getInstance/ getNewInstance and cannot be removed)
```js
injector.registerSecure("MySecureService", MySecureService);
```

<strong>Chaining</strong> registrations
```js
injector
	.register("MyService1", MyService1)
	.register("MyService2", MyService2);
```

<strong>Get</strong> an instance (create or get a cached instance)
```js
let instance = injector.getInstance("MyService");
```

<strong>Get a new</strong> instance
```js
let instance = injector.getNewInstance("MyService");
```

<strong>Invoke</strong> a function with Injector
```js
injector.invoke(myFunc);
```

### View usefull functions

Allow to <strong>select</strong> and <strong>animate</strong> HTML elements.

```js
import { qs, qsa } from "sparouter";

qs(".box").changeContent("<h1>New content</h1>");

qs(".box").animate("fadeIn",() => {
    // completed
});
```

---
_Source: https://npm.io/package/sparouter · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
