Router Basics
The Blits framework comes with a built-in, performant router that eliminates the need to pull in any additional dependencies into your App.
Before we dive into the details, let's first touch base on some key concepts.
Key concepts
Pages are regular Blits Components
The router in Blits is primarily used to navigate between Pages in your App. In the context of a Blits App, a Page is just a regular Blits Component. Usually it will be a Component that fills the entire screen. But it is also possible to have only a smaller portion of the screen contain dynamic routed content.
URL hash
When navigating to a page, the hash in the URL of the browser will automatically be updated (i.e. http://localhost:8080/#my/page
) to match the current page. The hash can also be used to deep-link directly into your application. Upon first load, the router functionality will evaluate the URL hash and try to match that to a defined route. If a matching route is found, it will load that page as deep linked content.
Defining Routes
Routes are defined in the main Application component, as an Array under the routes
key of the configuration object.
Each route in this array, is an Object literal that includes the following key properties:
path
- the url hash to the route (i.e./my/page
)component
- the Blits component to render for the route. This can be a direct reference, but also a promise or a dynamic import that resolves to a Blits componenthooks
(optional) - hooks such asbefore
can be defined to execute code before navigating to the routeoptions
(optional) - additional options defining route behaviour, such askeepAlive
andinHistory
transitions
(optional) - used to define custom transitions between pages
Dynamic routes with params
Besides static routes such as /account
and /settings/wifi/advanced
, the Blits router also supports dynamic routes where URI parts can contain params.
To define a param in a route path, prefix a string with a colon (:
), followed by the name of the param, e.g. /movies/:genre/:id
. This route path will match a URI hash such as #/movies/sci-fi/65281918
. The router will take the params and inject them as a prop
into the Page that's being navigated to.
To access these props in the Page component, they must first be defined in the Page component as props first (i.e. props: ['genre', 'id']
). Once defined, the values (sci-fi
and 65281918
) in this case, will be available on the this
scope as this.genre
and this.id
(or as $genre
and $id
when used in the template), as with any regular Component.
Using query parameters in routes
According to the browser specifications, query parameters are not part of a URL hash. This means that query parameters should be placed before the URL hash (i.e. http://localhost:5173?id=100&name=john#/my/page/hash
) and implies that "real" query parameters are not part of the route paths.
In this case id
and name
won't automatically be made available as props inside a Blits component. They can however be retrieved using document.location.search
in combination with new URLSearchParams
, and can be used in an App's index.js
to set dynamic launch settings for example.
Sometimes you may actually want pass custom data into a Blits component, without it being part of the dynamic route path or data
-object during navigation. For these cases Blits allows you to use query parameters as part of a route (i.e. #/series/simpsons/5/10?id=100&name=john
).
This URL hash will match the route /series/:show/:season/:episode
and it will pass id
and name
as additional props into the Page component. Note: similar to dynamic path params, route query params should also be be defined in the Page component as props first (i.e. props: ['id', 'name']
) in order to be accessed on the this
-scope.
Router view
The routes are loaded into a <RouterView />
component, which acts as a placeholder for the routed Page components.
The <RouterView />
component can be positioned and sized as you would expect from a normal Blits Component.
export default Blits.Application({
template: `
<Element>
<RouterView x="300" y="200" w="1520" h="680" />
</Element>
`,
routes: [
{ path: '/', component: Home },
{ path: '/details', component: () => import('./pages/Details.js'), },
{ path: '/account',
component: () => {
// imagine this is an API call or some other async action
return new Promise((resolve) => {
resolve(Account)
})
}
}
],
// ...
})
Navigation
Each component in a routed Blits app has a this.$router
object that provides access to the Router instance. It can be used to programmatically navigate to pages, by calling the to()
method on it.
The this.$router.to()
-methods accepts 3 arguments:
path
- the path of the route to navigate todata
(optional) - an object with data to pass into the page asprops
options
(optional) - additional options defining route behaviour, such askeepAlive
andinHistory
(these will overwrite any options specified for a route in the routes array)
export default Blits.Component('Poster', {
input: {
enter() {
this.$router.to('/movie/details', {id: '1', img: 'details.png'}, {inHistory: false})
}
}
})
Whenever you navigate to a new page, the URL hash will automatically be updated. Unless specified otherwise, navigating to a new page, will add that route to the history stack. The back
input action is automatically wired up to navigate back down the history stack.
Deeplinking
The Router plugin has support for deeplinking. When the App is loaded with a URL hash (i.e. #/pages/settings/network
), the router will try to match that hash to a defined route. This means that your app can be deep linked into, by simply providing the correct URL hash.
This is an important feature for several reasons:
- It will allow external sources (like operators and UIs) to link directly to a specific page in your app (i.e. the details page of a specific movie)
- If dynamic routes are used, the App only loads what is needed, to keep memory usage low and the initial load time fast
- Data provided in the URL hash can still be used to set the initial state of the App
Backtracking
When a user enters your App via a deeplink, there is technically no history available. By default, this means that a Back keypress goes straight to the App's root
-route (i.e. the Blits.Application()
-component).
In some cases, you may want walk back down the logical path of the deeplinked page instead, and navigate to the first existing parent route (i.e. go from /movies/comedy/american-pie
to /movies/comedy
).
By setting the route option backtrack
to true
in the route definition (or in the this.$router.to()
-method), the router will recursively remove the last part of the route hash, until it finds a valid path to navigate to.
{
path: '/examples/router-hooks/episode/:id',
component: Episode,
transition: PageTransitions.slideInOutUp,
options: {
backtrack: true,
},
},
{
path: '/examples/router-hooks/episode',
component: EpisodesOverview,
},
In the example above, the backtrack
option is set to true
for the /examples/router-hooks/episode/:id
route. When the user navigates to /examples/router-hooks/episode/1
, the back
input action will navigate to /examples/router-hooks/episode
instead of exiting the App.
Router API
The Router API provides several useful methods and properties for managing routes and navigation:
this.$router.to()
- navigating to a different location (as discussed above)this.$router.back()
- programmatically navigate back down the history stackthis.$router.currentRoute
- retrieve the current routethis.$router.routes
- retrieve the list of all routesthis.$router.navigating
- retrieve the current navigating statethis.$router.state
- reactive router state (see below)
Reactive router state
The reactive router state (this.$router.state
) can be useful in situations where you want to hook up reactive changes in your App to Route changes.
The state
variable on the this.$router
object returns a reactive object with 2 keys: path
and navigating
. The values of these keys will automaticaly update when the router navigates from one page to another.
The router state changes can be used in a Blits template, they can be watched and they can be used in generic busines logic, as demonstrated in the example below.
export default Blits.Component('MyComponent', {
template: `
<Element w="1920" h="1080">
<!-- dynamically display the current path -->
<Text x="100" h="100" :content="$$router.state.path" />
<!-- show loading text when router is navigating -->
<Text :show="$showLoader">Loading ...</Text>
</Element>
`,
watch: {
'$router.state.path'(v, old) {
Log.info(`Router Path changed from ${old} to ${v}`)
}
},
computed: {
showLoader() {
return this.$router.state.navigating
}
}
})