Class: Router

Oracle® JavaScript Extension Toolkit (JET)
5.0.0

E90577-01

QuickNav

Fields

Router

Version:
  • 5.0.0
Since:
  • 1.1.0
Module:
  • ojrouter

JET Router

The router is designed to simplify writing navigation for Single Page Applications. The approach taken is to think of navigation in terms of states and transitions instead of URLs and hashes. A router is always in one in a number of possible states and when a UI action is taken in the application, a transition between states is executed. The router is responsible to properly format the URL to reflect the current state and to restore the application to the matching state when the URL changes.

Building navigation is done in three steps:

Define the states that can be taken by the router:

var router = oj.Router.rootInstance;
// Add three states to the router with id 'home', 'book' and 'tables
router.configure({
   'home':   { label: 'Home',   value: 'homeContent', isDefault: true },
   'book':   { label: 'Book',   value: 'bookContent' },
   'tables': { label: 'Tables', value: 'tablesContent' }
});

var viewModel = {
   router: router
};

oj.Router.sync().then(
   function() {
      ko.applyBindings(viewModel);
   },
   function(error) {
      oj.Logger.error('Error when starting router: ' + error.message);
   });
Trigger a state transition when user ask to navigate:

<div id="routing-container">
   <div id='buttons-container' data-bind="foreach: router.states">
     <!-- Use the go function of the state as the handler for a click binding -->
     <input type="button"
            data-bind="click: go,  attr: {id: id},
            ojComponent: {component: 'ojButton', label: label}"/>
   </div>
</div>
Listen to the state change and updates the dependent parts:

<!-- Display the content of the current state -->
<h2 id="pageContent" data-bind="text: router.currentValue"/>
A Router cannot be instantiated. A static Router is created when the module is loaded and can be accessed using the method rootInstance. A child router can be created using the method createChildRouter.
See:

Requires

  • module:ojs/ojcore
  • module:knockout

Classes

urlParamAdapter
urlPathAdapter

Fields

(readonly) currentState :function(): (oj.RouterState|undefined)

A Knockout observable on the current RouterState object.
Example

Hide a panel when the state of the router is not yet defined:

   <div data-bind="if: router.currentState()">
      <!-- content of the panel -->
   </div>

(readonly) currentValue :function(): (string|undefined)

A Knockout observable on the value property of the current state.
The state value property is the part of the state object that will be used in the application. It is a shortcut for router.currentState().value;
Example

Display the content of the current state:

<h2 id="pageContent" data-bind="text: router.currentValue"/>

defaultStateId :string|undefined

The state id of the default state for this router. The value is set when configure is called on the router and the state isDefault property is true. If it is undefined, the router will start without a state selected. This property is writable and can be used to set the default state id when the router is configured using a callback.

(static) defaults

A set of Router defaults properties.
Warning:
Defaults can not be changed after the first call to sync() has been made. To re-initialize the router, you need to call dispose() on the rootInstance first then change the defaults.
Properties:
Name Type Argument Description
urlAdapter oj.Router.urlPathAdapter | oj.Router.urlParamAdapter <optional>
an instance of the url adapter to use. If not specified, the router will be using the path url adapter. Possible values are an instance of oj.Router.urlPathAdapter or oj.Router.urlParamAdapter.
baseUrl string <optional>
the base URL to be used for relative URL addresses. The value can be absolute or relative. If not specified, the default value is '/'.
Warning: When using the path URL adapter it is necessary to set the base URL if your application is not using index.html or is not starting at the root folder. Using the base URL is the only way the router can retrieve the part of the URL representing the state.
rootInstanceName string <optional>
the name used for the root router. If not defined, the name is 'root'. This is used by the urlParamAdapter to build the URL in the form of /index.html?root=book.
Examples

Change the default URL adapter to the urlParamAdapter

oj.Router.defaults['urlAdapter'] = new oj.Router.urlParamAdapter();

Set the base URL for an application located at the root and a starting page named index.html. This is the default.

oj.Router.defaults['baseUrl'] = '/';

Set the base URL for an application with a page named main.html and located in the /myApp folder.

oj.Router.defaults['baseUrl'] = '/myApp/main.html';

Change the default root router name to 'id'

oj.Router.defaults['rootInstanceName'] = 'id';

(readonly) direction :string|undefined

PREVIEW: This is a preview API. Preview APIs are production quality, but can be changed on a major version without a deprecation path.

The current navigation direction of the router, used for module animations. The value will either be undefined if navigating forward, or "back" if navigating backwards. This is the same value available as part of moduleConfig.
Since:
  • 5.0.0

(readonly) moduleConfig :Object

An object to simplify integration between ojRouter and ojModule. Use this object to configure an ojModule where the module name is the router state. When the router changes state, ojModule will automatically load and render the content of a new module based on the name specified in the value or id property of the current RouterState.
The object moduleConfig provides the following functionality to the ojModule binding:
  1. it defines the name of the module by setting the name option to the value of the current router state.
  2. it makes the parent router accessible to the module using the params['ojRouter']['parentRouter'] property.
  3. it defines a direction hint that can be use for the module animation.
  4. it makes the callback canExit invokable on the viewModel. If canExit is not defined on the viewModel, it will be invoked on the RouterState.
The moduleConfig object has the following properties:
  • name: is set to the value property of the current state of the router. If a current state is not defined, the default state is used and if the default state is not defined, the first state is used. On the state object, if the value is not defined or if it is not a string, the id property is used.
  • params: an object with a property named ojRouter which value is a object with two properties parentRouter which value is the parent router and direction which value is undefined or the string 'back'. In JET version 1.1, the parent router was the entire params object making it impossible to pass any other parameters. Application built using version 1.1 of JET now need to retrieve the parent router from the ojRouter.parentRouter property of params.
  • lifecycleListener: an object implementing the attached callback to bind canExit to the router if it is defined on the viewModel. If you override attached, this functionality will be lost unless you set the viewModel to the current state of the parent router in your custom attached implementation.
The router calculate the direction of the navigation and make it available to the child module using the parameter ojRouter.direction.
This can be used to specify a different module animation when going 'back'. The value of direction is either undefined or 'back'. It is 'back' when the state transition is caused by a back button on the browser and the new state is equal to the previous state. To customize the behavior of the moduleduleConfig object, it is possible to create your own moduleConfig and merge other properties or modifies the value of existing properties. It is recommended to use $.extend as described in the third example below.
Properties:
Name Type Description
name KnockoutObservable.<string>
params Object
Properties
Name Type Description
ojRouter Object
Properties
Name Type
parentRouter oj.Router
direction string
lifecycleListener Object
Properties
Name Type
attached function(string):void
Examples

Configure an ojModule binding with a router

<!-- This is where your main page content will be loaded -->
<div id="mainContainer" data-bind="ojModule: router.moduleConfig"></div>

Creates a child router in the viewModel of a module

var viewModel = {
   initialize: function(params) {
      // Retrieve the parent router from the parameters
      var parentRouter = params.valueAccessor().params['ojRouter']['parentRouter'];
      // Create a child router for this viewModel
      this.router = parentRouter.createChildRouter('chapter')
         .configure({
            'preface':  { label: 'Preface',   value: storage['preface']  },
            'chapter1': { label: 'Chapter 1', value: storage['chapter1'] },
            'chapter2': { label: 'Chapter 2', value: storage['chapter2'] },
            'chapter3': { label: 'Chapter 3', value: storage['chapter3'] }
         });
      oj.Router.sync();
   },

   // canExit callback will be called here
   canExit: function() {
      return (okToExit) ? true: false;
   }
};

Creates a custom moduleConfig replacing the name property

dynamicConfig = ko.pureComputed(function () {
   if (smallOnly()) {
      // Add the prefix 'phone/' to change the viewModel location
      return $.extend({},
                      router.moduleConfig,
                      {name: 'phone/' + router.moduleConfig.name()});
   }
   return router.moduleConfig;
});

(readonly, non-null) name :string

A string identifier of the router. It is required the name is unique within all the sibling routers.
See:

(readonly) observableModuleConfig :KnockoutObservable.<oj.Router.ModuleConfigType>

PREVIEW: This is a preview API. Preview APIs are production quality, but can be changed on a major version without a deprecation path.

Similar to oj.Router#moduleConfig, this object is meant to be used to configure a module binding which reacts to changes in the router state or its parameters. This configuration also creates observables around all of the oj.RouterState#parameters found in the router state.

This observable is dyanmically created only when it's first requested. To use observableModuleConfig and its observable parameters, first reference the property in your own code, which will setup the observable and add observable state parameters. To use the observable parameters, retrieve the instance from the parameters passed to your view model.

Since:
  • 4.2.0
Example

Use observable parameters

     var root = oj.Router.rootInstance;
     root.configure(...);
     var mc = root.observableModuleConfig;
     ...
     function ViewModel(viewParams) {
        var router = viewParams['ojRouter']['parentRouter'];
        var currentState = router.currentState();
        // Retrieve object containing observable parameters
        var params = viewParams['ojRouter']['parameters'];
        var employeeId = params['employeeId'];
        // Get an observable parameter's value
        var idValue = employeeId();

        // Optionally subscribe to a parameter's value change
        employeeId.subscribe(function(newId) {
        });
     }

(readonly) parent :oj.Router|undefined

The parent router if it exits. Only the 'root' router does not have a parent router.

(static, readonly) rootInstance :oj.Router

The static instance of oj.Router representing the unique root router. This instance is created at the time the module is loaded.
All other routers will be children of this object.
The name of this router is 'root'. To change this name use the rootInstanceName property.
Example

Retrieve the root router and configure it:

var router = oj.Router.rootInstance;
router.configure({
   'home':   { label: 'Home',   value: 'homeContent', isDefault: true },
   'book':   { label: 'Book',   value: 'bookContent' },
   'tables': { label: 'Tables', value: 'tablesContent' }
});

(readonly) stateId :function(string=): string

A Knockout observable for the id of the current state.
stateId() returns the string id.
stateId('book') transitions the router to the state with id 'book'.
It is convenient to use the stateId observable when working with component with 2-way binding like checked for ojButtonset or selection for ojNavigationList because it does not require a click on optionChange handler (See example below).
Example

A buttonSet using the router stateId for 2-way binding:

<div data-bind="ojComponent: { component: 'ojButtonset',
                               checked: router.stateId,
                               focusManagement:'none' }">
   <!-- ko foreach: router.states -->
      <label data-bind="attr: {for: id}"></label>
      <input type="radio" name="chapter"
             data-bind="value: id, attr: {id: id},
                        ojComponent: { component: 'ojButton',
                                       label: label}"/>
   <!-- /ko -->
</div>

(readonly) states :Array.<oj.RouterState>|null

An array of all the possible states of the router. This array is null if the router is configured using a callback.
See:

(static, readonly) transitionedToState :Object

A signal dispatched when the state transition has completed either by successfully changing the state or cancelling.
The parameter of the event handler is a boolean true when the state has changed.
This is usefull when some post processing is needed or to test the result after a state change.
Example

Creates promise that resolve when the state transition is complete.

var promise = new Promise(function(resolve, reject) {
      oj.Router.transitionedToState.add(function(result) {
         if (result.hasChanged) {
            oj.Logger.info('The state has changed');
         }
         resolve();
      });

Methods

(static) sync() → (non-null) {Promise.<{hasChanged: boolean}>}

Synchronise the router with the current URL. The process parse the URL and
  1. transition the router to a new state matching the URL.
  2. initialize the bookmarkable storage.
  3. dispatch a transitionedToState signal.
It has to be called after a router is configured, to synchronise the URL with the router state.
If a default state is defined, the router will transition to it, otherwise no transition will occur and the router will be in an undefined state.
Because the process of transitioning between two states invokes callbacks (canExit, canEnter) that are promises, this function also returns a promise.
Returns:
A Promise that resolves when the router is done with the state transition.
When the Promise is fullfilled, the parameter value is an object with the property hasChanged.
The value of hasChanged is:
  • true: If the router state changed.
When the Promise is rejected, the parameter value is:
  • An Error object stipulating the reason for the rejection when an error occurred during the resolution.
Type
Promise.<{hasChanged: boolean}>
Examples

Start the root instance

var router = oj.Router.rootInstance;
// Add three states to the router with id 'home', 'book' and 'tables
router.configure({
   'home':   { label: 'Home',   value: 'homeContent', isDefault: true },
   'book':   { label: 'Book',   value: 'bookContent' },
   'tables': { label: 'Tables', value: 'tablesContent' }
});

var viewModel = {
   router: router
};

oj.Router.sync().then(
   function() {
      ko.applyBindings(viewModel);
   },
   function(error) {
      oj.Logger.error('Error when starting the router: ' + error.message);
   }
);

Synchronise a newly created child Router and retrieve the bookmarkable state

 oj.Router.sync().then(
    function() {
       var color = viewModel.router.retrieve();
       if (color) {
          $('#chapter').css('background', color);
       }
    },
    function(error) {
       oj.Logger.error('Error during sync: ' + error.message);
    }
 );

configure(option)

Configure the states of the router. The router can be configured in two ways:
  • By describing all of the possible states that can be taken by this router.
  • By providing a callback returning a RouterState object given a string state id.
This operation resets any previous configuration, and is chainable.
Configuring router state parameters should be done here. See the example below.
Parameters:
Name Type Argument Description
option Object.<string, {oj.RouterState.ConfigOptions}> | function(string): (oj.RouterState | undefined) <not nullable>
Either a callback or a dictionary of states.
A callback:

stateFromIdCallback (stateId) → {oj.RouterState|undefined}

A function returning a RouterState given a string state id.
When using a callback, the states property will always be null since states are defined on the fly.
See second example below.
A dictionary of states:
It is a dictionary in which the keys are state ids and values are objects defining the state. Note that the forward slash character '/' is not allowed in the state Id. See ConfigOptions.
See first example below.
Key
Type Description
string the state id. See the RouterState id property.
oj.RouterState.ConfigOptions See ConfigOptions for the options available for configuring router states
See:
Examples

Add three states with id 'home', 'book' and 'tables':

router.configure({
   'home':   { label: 'Home',   value: 'homeContent', isDefault: true },
   'book':   { label: 'Book',   value: 'bookContent' },
   'tables': { label: 'Tables', value: 'tablesContent' }
});

Configure dynamic states via callback function:

router.configure(function(stateId) {
   var state;

   if (stateId) {
      state = new oj.RouterState(stateId, { value: data[stateId] }, router);
   }
   return state;
});

Configuring state parameters:

router.configure({
   'home':   { label: 'Home',   value: 'homeContent', isDefault: true },
   'book/{chapter}/{paragraph}':   { label: 'Book',   value: 'bookContent' },
});

createChildRouter(name, parentStateId) → {oj.Router}

Create a child router with the given name. A router can either have one child router that handles all possible states, or one child router per state. See the examples below.
Parameters:
Name Type Argument Description
name string <not nullable>
The unique name representing the router. The name is used by the function getChildRouter to retrieve the child router.
parentStateId string <optional>
The state Id of the parent router for determining when this child router is used. If not defined, the child router is created for the current state of the router.
Throws:
An error if a child router exist with the same name or if the current state already has a child router.
Returns:
the child router
Type
oj.Router
Examples

Create a default child router for the parent In this example, the parent router is assumed to have no current state (it has not yet navigated to any particular state). Since we are not specifying a value for parentStateId, the newly created router will be the default child router.

// Parent router has no current state
router = oj.Router.rootInstance;
// This child router is the default child router for all parent router states
childRouter = router.createChildRouter('chapter');

Create a child router for the root's current state In this example, the parent has navigated to a state before the child router is created. Even though no value is given for parentStateId, the child router is only used when the parent is in the particular state.

// Parent router navigates to a given state
router = oj.Router.rootInstance;
router.go('book').then(function(result) {
  if (result.hasChanged) {
    // Child router is only used when parent router's state is 'book'
    // because parent now has a current state
    var childRouter = router.createChildRouter('chapter');
  }
});

Create a child router for parent state id 'book' In this example, the parent router hasn't yet navigated to a particular state but the child specifies 'book' as its parentStateId, therefore, the child will only be used when the parent is in that particular state.

// Parent router has no current state
router = oj.Router.rootInstance;
// Child router is only used when parent router's state is 'book'
childRouter = router.createChildRouter('chapter', 'book');

dispose() → {undefined}

Dispose the router.
Erase all states of this router and its children. Remove itself from parent router child list.
When this method is invoked on the rootInstance, it also remove internal event listeners and re-initialize the defaults.
Returns:
Type
undefined

getChildRouter(name) → {oj.Router|undefined}

Return a child router by name. The name is the value given to createChildRouter.
Parameters:
Name Type Argument Description
name string <not nullable>
The name of of the child router to find
Since:
  • 1.2.0
Returns:
The child router
Type
oj.Router | undefined

getCurrentChildRouter() → {oj.Router|undefined}

PREVIEW: This is a preview API. Preview APIs are production quality, but can be changed on a major version without a deprecation path.

Get the child router associated with the parent's current state. See createChildRouter for details on how child routers are associated with parent states.
Since:
  • 5.0.0
Returns:
The child router for the current state, if defined.
Type
oj.Router | undefined

getState(stateId) → {oj.RouterState|undefined}

Return the oj.RouterState object which state id matches one of the possible states of the router.
Parameters:
Name Type Description
stateId string the id of the requested oj.RouterState object.
Returns:
the state object matching the id.
Type
oj.RouterState | undefined
Example

Retrieve the RouterState for id 'home':

var homeState = router.getState('home');
var homeStateValue = homeState.value;

go(stateIdPath, options) → (non-null) {Promise.<{hasChanged: boolean}>}

Go is used to transition to a new state using a path made of state ids separated by a slash. The path can be absolute or relative.

Example of valid path:
  • router.go('home'): transition router to state id 'home'
  • router.go('/book/chapt2'): transition the root instance to state id 'book' and the child router to state id 'chapt2'
  • router.go('chapt2/edit'): transition router to state id 'chapt2' and child router to state id 'edit'
  • router.go(['chapt2','edit']): equivalent to the previous transition, but using an array of path strings in place of forward slashes.

If the stateIdPath argument is undefined or an empty string, go transition to the default state of the router.
A transitionedToState signal is dispatched when the state transition has completed.
Parameters:
Name Type Argument Description
stateIdPath string | Array.<string> <optional>
A path of ids representing the state to which to transition, separated by forward slashes (/). This can also be an Array of strings, each segment representing individual states. An array is typically used if the forward slash should be part of the state Id and needs to be distinguished from the path separators.
options Object <optional>
an object with additional information on how to execute the transition.
Properties
Name Type Description
historyUpdate string Specify how the transition should act on the browser history. If this property is not specified, a new URL is added to the history.
Supported Values:
'skip': does not update the history with the new URL
'replace': modifies the current history with the new URL
Returns:
A Promise that resolves when the router is done with the state transition.
When the promise is fullfilled, the parameter value is an object with the property hasChanged.
The value of hasChanged is:
  • true: If the router state changed.
When the Promise is rejected, the parameter value is:
  • An Error object stipulating the reason for the rejection during the resolution. Possible errors are:
    • If stateIdPath is defined but is not of type string.
    • If stateIdPath is undefined but the router has no default state.
    • If a state id part of the path cannot be found in a router.
Type
Promise.<{hasChanged: boolean}>
Examples

Transition a router to the state id 'home':

router.go('home');

Transition a router to its default state and handle errors:

router.go().then(
   function(result) {
      if (result.hasChanged) {
         oj.Logger.info('Router transitioned to default state.');
      }
      else {
         oj.Logger.info('No transition, Router was already in default state.');
      }
   },
   function(error) {
      oj.Logger.error('Transition to default state failed: ' + error.message);
   }
);

Transition a router to state id 'stepB' without updating the URL:

wizardRouter.go('stepB', { historyUpdate: 'skip' });

retrieve() → {*}

Retrieve the additional data stored in the URL.
Returns:
the content stored in the URL
Type
*
Example

Retrieve the value of the background color stored in the URL:

 oj.Router.sync().then(
    function() {
       var color = viewModel.router.retrieve();
       if (color) {
          $('#chapter').css('background', color);
       }
    },
    function(error) {
       oj.Logger.error('Error during sync: ' + error.message);
    }
 );

store(data) → {undefined}

Store additional data for this router that will be added in a compressed form to the URL so it can be bookmarked. When calling this method, the URL is immediately modified.
Parameters:
Name Type Argument Description
data Object <not nullable>
the data to store with this state.
Throws:
An error if the bookmarkable state is too big.
Returns:
Type
undefined
Example

Store a color in the URL:

try {
   var color = '#99CCFF';
   router.store(color);
   $('#chapter').css('background', color);
}
catch (error) {
   oj.Logger.error('Error while storing data: ' + error.message);
}

Type Definitions

ModuleConfigType

Properties:
Name Type Description
name KnockoutObservable.<string> The value of the current state. See oj.Router#currentValue
params Object
Properties
Name Type Description
ojRouter Object
Properties
Name Type Description
parentRouter oj.Router A reference to the current router
direction string The animation direction of the module, if defined.
parameters Object An object containing the observable parameters of the current router state.
lifecycleListener Object
Properties
Name Type Description
attached function(Object):void The 'attached' listener for the module