Routing

The routing system in the framework is presented separately from the components and is located in the Akili.services.router. The key part of the router is states, which are responsible for managing a specific URL in the browser.

import router from 'akili/src/services/router';
console.log(router === Akili.services.router); // true

router.add(stateName, pattern, options)

  • stateName string - router state name
  • pattern string - url pattern to link with the state
  • options Object - other state options

Add new state to the router.

router.add('app', '/app', {
  handler: (transition) => {
    return 'App data';
  }
});

Above we have created an abstract state. It is abstract because doesn't have template. On the handler you can get transition - object with full information about current routing stage. It is an instance of router.Transition class. The last transition is always in router.transition property too.

router.init(defaultUrl, hashHistoryMode)

  • [defaultUrl] string - default url
  • [hashHistoryMode=true] string - use hash history mode or window.history

Initialize the router.

document.addEventListener('DOMContentLoaded', () => {
  router.init('/app', false);
  Akili.init().catch((err) => console.error(err));
});

The router must be initialized before Akili.

Transition

transition.path is the last path of all states found. if the parent path exists, it will be in the transition.path.parent property. And so on until all parts will be over. In order for you to understand how it works look at the examples below.

router.add('app.home', '/home', {
  handler: (transition) => {
    return 'App home data';
  }
});
router.add('app.home.hello', '/hello', {
  handler: (transition) => {
    return 'App home hello data';
  }
});

So now we have three states. Points in the name separate the parent states. Accordingly, the complete URL pattern will be the result of combining the pattern of some state and its parent.

  • app => /app
  • app.home => /app/home
  • app.home.hello => /app/home/hello

When we return something in the handler function, we add it to the corresponding path of transition to the data property. For example, if we move to app.home.hello state we will have:

  • transition.path.state.name === 'app.home.hello'
    transition.path.data === 'App home hello data'
  • transition.path.parent.state.name === 'app.home'
    transition.path.parent.data === 'App home data'
  • transition.path.parent.parent.state.name === 'app'
    transition.path.parent.parent.data === 'App'

In transition.routes you can find all of paths as array, transition.states is the same as transition.routes but includes only states, without other information.

router.state(state, params, query, hash, options)

  • state string | Object - router state object or name
  • [params] Object - url params
  • [query] Object - url query
  • [hash] string - url hash
  • [options] Object - state options

Go to the necessary route by state.

router.state('app.home.hello');
State params

In order to send any dynamic parameters to the route, you can use params.

router.add('app.outside', '/outside/:place/:time', {
  handler: (transition) => {}
});

router.state('app.outside', { place: 'library', time: 'afternoon' });
// => /app/outside/library/afternoon
State query

In order to add query to URL use query argument.

router.state('app.outside', { place: 'bar' }, { gender: 'male', eyes: 'black' });
// => /app/outside/bar?gender=male&eyes=black
State hash

If you need to add hash to URL use hash argument.

router.state('app.outside', { place: 'station' }, {}, 'platform1');
// => /app/outside/station#platform1

router.location(url, options)

  • url string - URL
  • [options] Object - state options

Go to the necessary route by url.

Every time you change the state an event occurs in window.

window.addEventListener('state-change', (e) => {
  console.log(e.detail === router.transaction); // true
});

When you change a route, but its parents coincide with the previous ones, they will not be updated again. Use the option { reload: true } for that.

router.state('app.outside', { place: 'station' }, {}, '', { reload: true });
router.location('/app/outside/station', { reload: true });

If you want to save the scroll position after the redirect, you need to use { saveScrollPosition: true } option.

Templates

In most cases when you work with routes you need to change not only the URL, but also the html content. To do this, you can use two options when you create a state.

router.add('app.car', '/car', {
  template: '<my-car-component>${ this.__transition.path.data }</my-car-component>',
  handler: () => {
    return 'Tesla model S'
  }
});

router.add('app.bus', '/car', {
  templateUrl: '/templates/bus.html'
});

class MyCarComponent extends Component {
  compiled() {
    console.log(this.scope.__transition);
  }
}
<div>
  <route></route>
</div>

So all the patterns of the current state will be embedded in components according to their level of nesting. Each route component scope.__transition contains all the necessary information about the corresponding transition. Therefore, in the state of the car inside the my-car-component component element will be Tesla model S.

Redirecting

If you want to redirect to other state in some handler you need to use transition.redirect.

router.add('app.flat', '/flat/:room', {
  handler: (transition) => {
    if(!transition.params.type) {
      transition.redirect('app.flat', { room: 'kitchen' });
    }
  }
});

To cancel transition use transition.cancel.

router.add('app.shapes', '/shapes', {
  handler: (transition) => {
    if(transition.query.shoes !== undefined) {
      transition.cancel();
    }
  }
});

router.back()

Go back on history.

router.forward()

Go forward on history.

router.go(position)

  • position integer - history position (window.history.go)

Go to the position.