Route Binding

One of the most common needs in a modern single page application is to facilitate a route/state change when clicking an element. An example of this would be a user clicking their profile page link in the header.

The easiest way of accomplishing this is via the route binding.

Basic Route Binding

<router module="Router">
  <a href="/profile" data-bind="route">Profile</a>
</router>

When a route binding is triggered it will traverse the DOM tree upwards until it finds its nearest parent router. It will then initiate the state change to the newly addressed route/state.

Note

  • By default the route binding will retrieve its destination url from the href property of the element it is attached to and triggers off of the click event.

    You can alter these defaults (and more) by providing a binding options configuration.

  • Any element can be bound to a route...it does not have to be an anchor tag.

Binding Lifecycle

When Footwork encounters a route binding:

  1. The binding is initialized.

    If no binding options object is provided then:

    • The element is inspected and its href property is used as the url value

    • Its handler is bound against the click event on the element.

  2. If the element is an anchor tag, then its href attribute is set to the url value.

  3. The binding is now active until the element is removed from the DOM. It will respond to the configured event on the element.

  4. Anytime the event is triggered, its nearest parent router state is changed per the route binding configuration.

    The method defined by the history option (pushState/replaceState) on the route binding is the one used to manipulate the route.

  5. The element is removed from the DOM, its handler is unsubscribed from the event and the binding is disposed of.

Binding Options

There are several options available when binding a route with an options object:

Callback Binding and Context

Binding a callback operates on the local context.

This means that when you bind a callback/handler in your DOM it will be binding against the local view model the element is bound against, which is not necessarily the parent router (the router is likely/usually higher in the DOM tree).

state (string | object)

The state written to the routers currentState (as well as browser history) when the user triggers the binding.

String Value

<a data-bind="route: '/profile'">Profile</a>

Object

<a data-bind="route: { state: '/profile' }">Profile</a>

Note

If this value is provided as a string it will be used to set the element href attribute value.

Search engines will then be able to use this when indexing the document. This also means users will be able to hover their mouse over to inspect, right-click to open in new window, or copy/paste any route bound link (just as they would any other normal link).

on (string)

You can specify a different event upon which the binding is bound/triggered (default is click):

<a data-bind="route: { on: 'dblclick' }" href="/profile">Profile</a>

activeClass (string | callback)

Any element bound to the currently active route will have this class applied to it. The default being: active

String Value

<a data-bind="route: { activeClass: 'active' }" href="/profile">Profile</a>

Callback Function

You can also provide a callback function which returns the class to use:

<a data-bind="route: { activeClass: active }" href="/profile">Profile</a>
viewModel.active = function () {
  return 'is-active';
}

This callback is triggered anytime the active route changes and matches the bound url (or when the element is initially bound, if the route already matches upon initialization).

history (string | callback)

By default a pushState call is made when the binding is triggered. You can alter this by providing either push or replace with this value.

String Value

<a data-bind="route: { history: 'replace' }" href="/profile">Profile</a>

Callback Function

If you provide a callback function, its return value will be used to tell Footwork which history method to use:

<a data-bind="route: { history: whichHistory }" href="/profile">Profile</a>

The callback is provided the browser event used to trigger the binding along with the destination url:

self.whichHistory = function (event, url) {
  return 'replace';
}

This callback is evaluated whenever the binding is triggered.

handler (callback)

The handler called whenever the on event is triggered:

<a data-bind="route: { handler: goToProfile }" href="/profile">Profile</a>

This handler should return true or false, indicating whether or not the click is valid and should be routed to. Think of the callback as a predicate function for the route.

The callback is provided the browser event used to trigger the binding along with the destination url:

self.goToProfile = function (event, url) {
  // preventDefault prevents the browser from loading the new url
  event.preventDefault();

  if (url === '/profile' && isLoggedIn) {
    return true; // route controller will be executed
  } else {
    return false; // nothing will happen
  }
};

You can also return a url to route to from the callback:

self.goToProfile = function (event, url) {
  // preventDefault prevents the browser from loading the new url
  event.preventDefault();

  if (url === '/profile' && isLoggedIn) {
    return '/profile-page'; // set the state and route to /profile-page (as opposed to /profile)
  } else {
    return false; // nothing will happen
  }
};