Method | What it does |
---|---|
Frequently used | |
constructor |
|
on |
Registers a route |
navigate |
Navigates to a route with a change of the browser URL. You usually are calling this as a result of user interaction. You want to change the URL. |
resolve |
Navigates to a route but it doesn't change the browser URL. You should fire this at least one in the beginning. |
notFound |
Defining a not-found handler |
Other methods | |
navigateByName |
Navigate to a route by having its name and supplying URL data args |
off |
Removes a registered route |
match |
Checks if the passed path matches some of the routes. It doesn't trigger handlers or hooks. |
matchLocation |
The bare matching logic of Navigo |
destroy |
Removing the currently registered routes |
updatePageLinks |
Call this if you re-render (change the DOM) and want Navigo to recognize the links with data-navigo attribute |
link |
Constructs a path |
generate |
Constructs a path based on a registered route |
lastResolved |
Returns the last resolved route/s |
hooks |
Define all-routes hooks |
addBeforeHook |
Adding before hook on an already created route |
addAfterHook |
Adding after hook on an already created route |
addAlreadyHook |
Adding already hook on an already created route |
addLeaveHook |
Adding leave hook on an already created route |
getCurrentLocation |
Returns a Match object for the current browser location |
getRoute |
Get a Route by name/path |
- API
- Topics
- Initializing
- Adding a route
- Removing a route
- Navigating between routes
- Augment your
<a>
tags - Resolving routes
- Direct matching of registered routes
- Direct matching of paths
- Hooks
- Destroying the router
- Generating paths
- Handling a not-found page
- Getting current location of the browser
- Getting access to a route
- Types
Types
Object |
---|
Navigo |
Match |
Route |
RouteHooks |
RouterOptions |
NavigateOptions |
ResolveOptions |
Navigo constructor has one mandatory argument - the root path of your application. For example, if you are hosting the application at https://site.com/my/project
you have to specify the following:
const router = new Navigo('/my/project');
The second argument is the default router options. The strategy
field there defines how many matches the router finds - one or many.
const router = new Navigo('/my/project', { strategy: 'ALL' });
By default the strategy is equal to "ONE"
. Meaning that when a match is found the router stops resolving other routes.
Another option available is noMatchWarning
. Which if you set to true
will prevent the router of warning a message if there is no matching route.
interface Navigo {
on(path: string, f: Function, hooks?: RouteHooks): Navigo;
on(f: Function, hooks?: RouteHooks): Navigo;
on(map: Object, hooks?: RouteHooks): Navigo;
}
To add a route use the on
method. It can be used in four different ways. The first and the most straightforward one is when you have a path and want to map it to a function.
const router = new Navigo('/');
router.on('/foo/bar', () => {
// Fired if the page URL matches '/foo/bar'.
});
The path in this case could be also a regular expression. For example:
const router = new Navigo('/');
router.on(/foo\/(.*)/, () => {
// Fired if the page URL matches '/foo/bar'.
});
If you skip the path you are basically defining a handler for your root.
const router = new Navigo('/my/app');
router.on(() => {
// Fired if the page URL matches '/my/app' route.
// Or in other words the home of your app.
})
The on
method is chainable so you can call on('...', () => {}).on('...', () => {})
if you want.
The next option is to define a key-value pairs of your routes:
router.on({
'/foo/bar': () => {
// Fired if the route matches '/foo/bar'.
},
'/foo/zar': () => {
// Fired if the route matches '/foo/zar'.
}
});
And the most complex one is by giving your route a name (via the as
field). This could be used in a combination of the generate method to construct a page path or simply to identify what is currently matching.
router.on({
'/foo/bar': {
as: 'routeA',
uses: () => {
// Fired if the route matches '/foo/bar'.
}
}
'/foo/bar': {
as: 'routeB',
uses: () => {
// Fired if the route matches '/foo/bar'.
}
}
})
Notice the typing of the on
method in the beginning of this section and you'll see that you can pass hooks to each route. More about that in the hooks section.
The parameterized routes have paths that contain dynamic parts. For example:
const router = new Navigo('/');
router.on('/user/:id/:action', ({ data }) => {
console.log(data); // { id: 'xxx', action: 'save' }
});
router.resolve('/user/xxx/save');
"/user/xxx/save"
matches the defined route. "xxx"
maps to id
and "save"
to action
. The data from the URL comes into the data
field of the Match object passed to your handler.
Parameterized routes happen also when we use a regular expression as a path. It's just our data
property comes as an array containing the matched groups.
const router = new Navigo('/');
router.on(/rock\/(.*)\/(.*)/, ({ data }) => {
console.log(data); // ["paper", "scissors"]
});
router.resolve("/rock/paper/scissors");
Navigo captures the GET params of the matched routes. For example:
const router = new Navigo('/');
router.on('/user/:id/:action', ({ data, params, queryString }) => {
console.log(data); // { id: 'xxx', action: 'save' }
console.log(params); // { m: "n", k: "z" }
console.log(queryString); // "m=n&k=z"
});
router.resolve('/user/xxx/save?m=n&k=z');
Navigo captures the hash bit of the URL for the matched routes. For example:
const router = new Navigo('/');
router.on('/foobar', ({ hashString }) => {
console.log(hashString); // something-else
});
router.resolve('/foobar#something-else');
Navigo relies on regular expressions to match strings against location paths. This logic is abstracted and for the final user we have a simple DSL. Here are couple of examples:
router.on('/foo', () => {});
// matches specifically "/foo"
router.on('/foo/:name', () => {});
// matches "/foo/my-name-here"
router.on(':page', () => {});
// matches "/about-page"
router.on('/foo/*', () => {});
// matches "/foo/a/b/c"
router.on('*', () => {});
// matches "/foo/bar/moo"
router.on(/rock\/(.*)\/(.*)/, () => {});
// matches "/rock/paper/scissors"
router.on('/foo/:id/?', () => {});
// matches "/foo/20/save" and also "/foo/20"
interface Navigo {
off(path: string | RegExp): Navigo;
off(handler: Function): Navigo;
}
To remove a route call the off
method by passing the path (or the used regular expression) or the handler of the route.
interface Navigo {
navigate(to: string, options?: NavigateOptions): void;
}
type NavigateOptions = {
title?: string;
stateObj?: Object;
historyAPIMethod?: string;
updateBrowserURL?: boolean;
callHandler?: boolean;
callHooks?: boolean;
updateState?: boolean;
force?: boolean;
resolveOptions?: ResolveOptions;
};
The navigate
method by default:
- Checks if there is a match. And if the answer is "yes" then ...
- It calls hooks (if any) and your route handler.
- Updates the internal state of the router.
- Updates the browser URL.
Consider the following example:
const router = new Navigo("/");
router
.on("/foo/bar", () => {
console.log('Nope');
})
.on("/about", () => {
console.log('This is About page');
});
router.navigate("about");
After the last line the browser will have in its address bar /about
as a path and in the console we'll see "This is About page"
. router.lastResolved()
and router.current
will point to an array of a single object of type Match.
navigate
accepts a few options:
title
is a string that gets passed topushState
(orreplaceState
).stateObj
is a state object that gets passed topushState
(orreplaceState
).- If you don't want to have a new entry in the history you should pass
{ historyAPIMethod: 'replaceState' }
. By default ispushState
. - If
updateBrowserURL
is set tofalse
the library will not use the history API at all. Meaning, the browser URL will not change. - If
callHandler
is set tofalse
your route handler will not be fired. - If
callHooks
is set tofalse
your route hooks will be skipped. - If
updateState
is set tofalse
the router will not update its internal state. This means that thelastResolved()
/current
route will not be updated. - If
force
is set totrue
the router will update its internal state only. This makes the router like it already resolved specific URL. resolveOptions
are the same options used in the resolve method.
interface Navigo {
navigateByName(name: string, data?:Object, options?: NavigateOptions): boolean;
}
Very often we have complex URLs and we want to have a quick way to reach them. navigateByName
is a method that helps in such cases:
route.on({
"/users/:name": {
as: "user",
uses: (match) => {
console.log(match.data.name); // Krasimir
}
},
});
router.navigateByName("user", { name: "Krasimir" });
/users/Krasimir
is set in the address bar of the browser and the handler is executed.
The method returns true
if there is such route with that name (i.e. the navigating happened) and false
if it doesn't.
Let's say that we have a page with links (<a>
tags). The links have href
attributes pointing to locations inside your app. By default Navigo doesn't know about those links and if you click them you'll probably get a full page load. To augment those links and integrate them with Navigo just add data-navigo
attribute. For example:
<a href="/company/about" data-navigo>About</a>
When Navigo is initialized checks the page for such tags and attaches click
handler which fires the router's navigate
method.
Navigo has a method called updatePageLinks
which you have to call every time when you change the DOM and you expect to see new links on the page. Because Navigo is not wired to a rendering engine doesn't really know about the DOM manipulations. It does though makes an assumption - after each of your route handlers there is a updatePageLinks
call. The router expects that after the successful route resolving the DOM is updated and calls that function again. Feel free to fire updatePageLinks
multiple times on the same DOM tree. There will be just one click
handler attached to your links.
Links with target="_blank"
are ignored even if they have data-navigo
attribute.
If you use data-navigo="false"
the link will be ignored by Navigo and if there was a click handler for it the handler will be removed.
If you are not happy with data-navigo
attribute you may provide your own CSS selector. You may even say "just augment all the links on the page".
const router = new Navigo("/", { linksSelector: "a" });
Example here.
As we learned above, when a link with data-navigo
attribute is clicked the navigate
method of the router gets executed. That same method accepts options and if you want to pass some of them use the following syntax:
<a href="/foo/bar" data-navigo data-navigo-options="updateBrowserURL:false, callHandler: false, updateState: false, force: false, historyAPIMethod: replaceState, resolveOptionsStrategy: ALL, resolveOptionsHash: true">my link</a>
Which will result in the following options:
{
updateBrowserURL: false,
callHandler: false,
updateState: false,
force: false,
historyAPIMethod: "replaceState",
resolveOptions: { strategy: "ALL", hash: true },
}
interface Navigo {
resolve(path?: string, resolveOptions?: ResolveOptions): false | Match[];
}
export type ResolveOptions = {
strategy?: ONE | ALL;
noMatchWarning?: true | false;
};
type Match = {
url: string;
queryString: string;
route: Route;
data: Object | null;
params: Object | null;
};
type Route = {
name: string;
path: string;
handler: Function;
hooks: RouteHooks;
};
By default Navigo is not resolving your routes. You have to at least once call resolve
method. The path
argument is not mandatory. If you skip it the library will use the current URL of the page. The method is fired automatically in the following cases:
- If there is a
popstate
event dispatched (this happens when the user manually changes the browser location by hitting for example the back button) - If the
navigate
method is called andshouldResolve
is not set tofalse
By default the resolve
function catches the first match and stops searching. You can amend this behavior by passing "ALL"
as a value of the strategy
field. However, if there is a matching route you'll get an array of object (or objects) of type Match. If there is no match then resolve
returns false
. When your route gets resolved its handler is called. It receives a Match object. From that object you can pull the data passed through the URL (if you used a parameterized path) or the GET params set in the URL.
If you need to see the latest match (or matches) you can access it via the lastResolved()
method.
resolve
does the following:
- Checks if there is a match. And if the answer is "yes" then ...
- It calls hooks (if any) and your route handler.
- Updates the internal state of the router.
Another option available is noMatchWarning
. Which if you set to true
will prevent the router of warning a message if there is no matching route.
export type ResolveOptions = {
strategy?: ONE | ALL;
hash?: boolean;
noMatchWarning?: true | false;
};
strategy
- either"ONE"
(by default) or"ALL"
.hash
- whether to use hash based routing or not (by default isfalse
).noMatchWarning
-false
(by default) ortrue
Navigo supports hash based routing. Which means that it uses the hash string as path for routing. For example /my/app/#/about/team
is treated as /about/team
. To enable this mode you have to pass hash: true
when creating the router.
const router = new Navigo('/', { hash: true });
If you want to check if some path is matching any of the routes without triggering hooks, handlers or changing the browser URL you may use the match
method. For example:
const r: Navigo = new Navigo("/");
r.on("/user/:id", () => {});
console.log(r.match("/nope"));
// result: false
console.log(r.match("/user/xxx/?a=b"));
// result:
// [
// {
// data: {
// id: "xxx",
// },
// params: { a: "b" },
// queryString: "a=b",
// route: {
// handler: [Function],
// hooks: undefined,
// name: "user/:id",
// path: "user/:id",
// },
// url: "user/xxx",
// }
// ]
The function returns an array of Match objects or false
.
There is a matchLocation
method that offers the bare matching logic of the router. You pass a path
and it checks if the string matches the current location. If you don't want to use the current location of the browser you may send a second argument. For example:
// let's say that the path of the browser is "/foo/bar?a=b"
router.matchLocation('/foo/:id');
/*
{
data: {
id: "bar",
},
params: { a: "b" },
queryString: "a=b",
route: {
handler: expect.any(Function),
hooks: {},
name: "foo/:id",
path: "foo/:id",
},
url: "foo/bar",
}
*/
// passing the current location manually
r.matchLocation("/foo/:id", "/foo/bar?a=b");
The method returns false if there is no match.
The hooks are functions that are fired as part of the resolving process. Think about them as lifecycle methods.
The hooks object has the following signature:
type RouteHooks = {
before?: (done: Function, match: Match) => void;
after?: (match: Match) => void;
leave?: (done: Function, match: Match) => void;
already?: (match: Match) => void;
};
- The
before
hook receives two arguments. The first one is a function that needs to be called with either no arguments orfalse
. The no-argument version means "move forward".false
stops the resolving and your handler will not be called. after
is called after your handlerleave
is called when you are about to leave out of the route. Similarly to thebefore
hook accepts a function as first argument and a Match object (or an array of Match objects) as second. If the function is called withfalse
Navigo will stop resolving the new matched route meaning "we cant' go out of the current route".already
is called when this is the current route and it matches again
The on
method accepts a hooks object as a last argument. For example:
// for specific path
router.on('/foo/bar/', () => {...}, {
before(done, match) {
// do something
done();
}
});
// for the root
router.on(() => {...}, {
before(done, match) {
// do something
done();
}
});
// when using a route map
r.on({
"/foo/:id": {
as: "some.name",
uses: handler,
hooks: {
before: (done) => {
// do something
done();
},
},
},
});
// with the notFound method
router.notFound(() => {...}, {
before(done, match) {
// do something
done();
}
});
You can define hooks that will be used for all the registered routes. It is important to set the hooks before the other route definition.
const router = new Navigo("/");
router.hooks({
before(done, match) {
// do something
done();
}
});
router.on("/foo/bar", () => {});
router.on("/", () => {});
Sometimes you may need to add a hook to a route later. In such cases use the following syntax:
const router = new Navigo("/");
router.on("/foo/bar", () => {});
const cleanup = router.addBeforeHook('foo/bar', (done) => {
// my before hook logic
})
// ... some other logic
cleanup();
With addBeforeHook
we have addAfterHook
, addAlreadyHook
and addLeaveHook
methods. All of them return a function which if called will remove the hook from the specific route.
destroy(): void;
If you no longer need Navigo working just call router.destroy()
. This will flush all the registered routes so nothing will match.
There are two methods link
and generate
that you can use to create string paths. For example:
const router = new Navigo('/my/app');
router.link('blah'); // "/my/app/blah
router.on({
"/user/:id/:action": { as: "RouteNameHere", uses: () => {} },
});
r.generate("RouteNameHere", { id: "xxx", action: "save" }); // "/my/app/user/xxx/save"
Notice that the produced strings by default start with the root that you passed to the Navigo's constructor. If you don't want this behavior pass a third argument to the function { includeRoot: false }
. In this case the second argument should be always present. Feel free to pass null
if you don't have any data.
interface Navigo {
notFound(handler: Function, hooks?: RouteHooks): Navigo;
}
Navigo offers a special handler for the cases where a no match is found.
const router = new Navigo('/');
router.notFound(() => {
// this runs if there is no match found
});
router.getCurrentLocation()
returns the current location of the browser in the format of a Match object.
There is getRoute
method that accepts a route name/path. The object that you receive is of type Route.
class Navigo {
constructor(root: string, options?: RouterOptions);
destroyed: boolean;
current: Match[];
routes: Route[];
on(f: Function, hooks?: RouteHooks): Navigo;
on(map: Object, hooks?: RouteHooks): Navigo;
on(path: string | RegExp, f: Function, hooks?: RouteHooks): Navigo;
off(path: string | RegExp): Navigo;
off(handler: Function): Navigo;
navigate(to: string, options?: NavigateOptions): void;
navigateByName(name: string, data?: Object, options?: NavigateOptions): void;
resolve(path?: string, resolveOptions?: ResolveOptions): false | Match;
destroy(): void;
notFound(handler: Function, hooks?: RouteHooks): Navigo;
updatePageLinks(): Navigo;
link(path: string): string;
lastResolved(): null | Match[];
generate(name: string, data?: Object, options:? GenerateOptions): string;
hooks(hooks: RouteHooks): Navigo;
getLinkPath(link: Object): string;
match(path: string): false | Match[];
matchLocation(path: string, currentLocation?: string, annotatePathWithRoot?: boolean): false | Match;
getCurrentLocation(): Match;
addBeforeHook(route: Route | string, hookFunction: Function): Function;
addAfterHook(route: Route | string, hookFunction: Function): Function;
addAlreadyHook(route: Route | string, hookFunction: Function): Function;
addLeaveHook(route: Route | string, hookFunction: Function): Function;
getRoute(name: string): Router | undefined;
}
type Match = {
url: string;
queryString: string;
hashString: string;
route: Route;
data: Object | null;
params: Object | null;
};
type Route = {
name: string;
path: string | RegExp;
handler: Function;
hooks: RouteHooks;
};
type RouteHooks = {
before?: (done: Function, match: Match) => void;
after?: (match: Match) => void;
leave?: (done: Function, match: Match | Match[]) => void;
already?: (match: Match) => void;
};
export type RouterOptions = ResolveOptions & { linksSelector?: string };
type NavigateOptions = {
title?: string;
stateObj?: Object;
historyAPIMethod?: string;
updateBrowserURL?: boolean;
callHandler?: boolean;
callHooks?: boolean;
updateState?: boolean;
force?: boolean;
resolveOptions?: ResolveOptions;
};
export type ResolveOptions = {
strategy?: ONE | ALL;
hash?: boolean;
noMatchWarning?: true | false;
};
export type GenerateOptions = {
includeRoot: boolean;
};