Skip to content

icariohealth/Backbone.Manager

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Backbone.Manager

Build Status Coverage Status Dependency Status devDependency Status

Backbone.Manager is a state-based routing/control manager for Backbone. It removes direct dependency on the router, and instead provides a standard control mechanism for url updates and state-change handling. It can be used for large state changes that involve url updates and moving between major view controllers, or for small state changes to do things like flash div content.

Goals

  • Intuitive state change
  • Differentiate between pageload and triggered changes
  • Remove temptation of view<->router relationships
  • Automatic state change from clicked anchors
  • Programmatic state change ability

This:

UsersRouter = Backbone.Router.extend
  routes:
    'users/:id': 'showUser'

  initialize: (options) ->
    _.bindAll @, 'switchToUser'

    @listenTo Backbone, 'showUser', @switchToUser

is now organized into this:

UsersManager = Backbone.Manager.extend
  states:
    'users.detail':
      url: 'users/:id'
      loadMethod: 'showUser'
      transitionMethod: 'switchToUser'

Usage

A Backbone.Manager instance is created by providing a router (required) to the constructor: new Backbone.Manager(router). If you're creating multiple Manager instances, it's recommended to just share a single instance of a router between them.

####Example

UsersManager = Backbone.Manager.extend
  states:
    users:
      url: 'users'
      loadMethod: 'showUsers'
      transitionMethod: 'switchToUsers'
    'users.detail'
      url: 'users/:id'
      loadMethod: 'showUser'
      transitionMethod: 'switchToUser'

  events:
    'loadSuccess:users.detail': 'prepareUser'
    'transitionStart': 'logToAnalytics'
    
  initialize: ->
    # ...
  showUsers: ->
    # ...
  switchToUsers: (searchString, options) ->
    # ...
  showUser: (id) ->
    # ...
  switchToUser: (id, searchString, options) ->
    # ...
  prepareUser: (id) ->
    # ...
  logToAnalytics: ->
    # ...

States

The states definition is the foundation of the Manager. It consists of state names paired with definitions for that state. States basically fall into one of two categories:

Which category a state falls under is controlled by the state being provided with an url definition:

states:
  urlState:
    url: '/states/:id'
    # etc
  nonUrlState:
    # etc

States with url definitions

These are able to be triggered via:

  • Initial Pageload
  • Window.popstate of '/users/1'
  • Backbone.Manager.go('users.detail',[1])
  • Backbone.Manager.goByUrl('/users/1')
  • Direct data-bb-state trigger: <a data-bb-state="users.detail([1])">
  • Inferred data-bb-state trigger: <a data-bb-state href="/users/1">

States without url definitions

These are able to be triggered via:

  • Programmatic: Backbone.Manager.go('users.detail',[1])
  • data-bb-state definition: <a data-bb-state="users.detail([1])">

loadMethod optional

states:
  'users.detail':
    url: 'users/:id'
    loadMethod: 'callback' # String representing method name for callback

Callback used immediately upon load of the page, when the page url matches defined url (User navigates directly). Url must be defined to activate.

# Arguments are built from url params, passed straight from the Router
callback: (id, searchString) ->

transitionMethod

Callback used when any non-loadMethod related state change occurs. Functionality can be affected by passing transitionOptions.

When url Is Defined

states:
  'users.detail.books.detail':
    url: 'users/:a/books/:b'
    transitionMethod: 'callback' # String representing method name for callback

Before the transitionMethod is triggered, A router.navigate will occur with the state's url. Note: currently anything inside of and including the optional matcher (()'s) in the state url are removed first.

Callback method takes the params in order from the url, then provides the searchString (provided because of the router, usually null), and finally an options object containing the populated url. So:

trigger callback method
Backbone.Manager.go('users.detail.books.detail',[1,2]) callback(1,2,null,{url: 'users/1/books/2'})
Backbone.Manager.go('users.detail.books.detail',{b:2,a:1})
(args order not important)
callback(1,2,null,{url: 'users/1/books/2'})
<a data-bb-state="users.detail.books.detail([1,2])"> callback(1,2,null,{url: 'users/1/books/2'})
<a data-bb-state="users.detail.books.detail({b:2,a:1})">
(args order not important)
callback(1,2,null,{url: 'users/1/books/2'})
Backbone.Manager.goByUrl('/users/1/books/2') callback(1,2,null,{url: 'users/1/books/2'})
<a data-bb-state href="/users/1/books/2"> callback(1,2,null,{url: 'users/1/books/2'})

When url Is NOT Defined

states:
  'users.detail.books.detail':
    transitionMethod: 'callback' # String representing method name for callback

Callback method takes the params in order as passed. Order is important, even when an object is used for the args. So:

trigger callback method
Backbone.Manager.go('users.detail.books.detail',[1,2]) callback(1,2)
Backbone.Manager.go('users.detail.books.detail',{b:2,a:1})
(args order important)
callback(2,1)
values taken in order
<a data-bb-state="users.detail.books.detail([1,2])"> callback(1,2)
<a data-bb-state="users.detail.books.detail({b:2,a:1})">
(args order important)
callback(2,1)
values taken in order

transitionOptions

Functionality of how the transition will occur can be controlled by passing in transitionOptions. These options can be passed directly using go or goByUrl, or also by setting data-bb-options on an anchor.

Currently supported options:

name description
navigate (boolean, default: true) If set to false, will not update the url when the transition is occurs, even if the url is provided for the state.

The '*' State

The '*' is reserved as a final matcher for states. When the data-bb-state watcher attempts to perform a state transition for a state that hasn't been defined, it will fallback to a '*' state definition. Here is an example of how to use it:

Backbone.Manager.extend
  states:
    '*':
      url: '*url'
      transitionMethod: 'defaultTransition'
  defaultTransition: (url) ->
    # ...

Important: If this is declared, it should be done within the very first manager created, and as the very first state definition. This is so that it ends up being placed in the bottom of the handlers stack within Backbone.History. When a Manager is created, Backbone.Manager inserts each url handler into the shared router as it progresses through the States definition... from the top down. The Router then works from the top down in its handlers when it's searching for a match. This is a Backbone.Router limitation.

Events

Backbone.Manager will trigger state specific and general events as the transition and load methods are being processed. These are async calls, so the callbacks aren't guaranteed to have completed before the post events are triggered. Here are the following events that are triggered:

event description
loadStart incoming page load call for any state
loadStart:[state] (args) incoming page load call for the [state]
(args) are the url params provided by the router
loadSuccess loadMethod callback completed
loadSuccess:[state] (args) loadMethod callback completed for the [state]
(args) are the url params provided by the router
loadError (error) loadMethod callback failed
loadError:[state] (args, error) loadMethod callback failed for the [state]
(args) are the url params provided by the router
(error) is the thrown error
transitionStart incoming transition call for any state
transitionStart:[state] incoming transition call for the [state]
transitionSuccess transitionMethod callback completed
transitionSuccess:[state] transitionMethod callback completed for the [state]
transitionError (error) transitionMethod callback failed
(error) is the thrown error
transitionError:[state] (error) transitionMethod callback failed for the [state]
(error) is the thrown error
exit state is transitioning out of the current Manager, into a different one
navigate Router's navigate function has been triggered and the url has changed

Triggering State Change

There are four different ways to trigger a state change within Backbone.Manager:

Initial Pageload

Triggered immediately upon load of the page, when the page url matches defined url (User navigates directly). Url must be defined to activate. This will trigger the loadMethod associated with the url.


Window.popstate

Typically occurs when the user uses the back button. This will trigger the transitionMethod associated with the url.


Backbone.Manager.go(stateName, args[, transitionOptions])

The programmatic way of triggering state changes. Example Usage:

events:
  'click dd': 'showUser'
showUser: ->
  Backbone.Manager.go('users.detail', {id:1}, {navigate: true})

params:

  • stateName
  • args: [] or {}
  • (optional) transitionOptions (Object containing the options defined in transitionOptions)

Backbone.Manager.goByUrl(url [, transitionOptions])

The programmatic way of triggering state changes via url matching. Example Usage:

events:
  'click dd': 'showUser'
showUser: ->
  Backbone.Manager.goByUrl('/users/1', {navigate: true})

params:

  • url (tested against url matchers defined in states, will use * state if none are found)
  • (optional) transitionOptions (Object containing the options defined in transitionOptions)

Click on <a data-bb-state>

The data-bb-state attribute is watched for by Backbone.Manager on all anchor tag clicks that bubble up to document. If event propagation is disabled or preventDefault gets set on that event, then Backbone.Manager will not trigger.

Example Usages:

<a data-bb-state='users.detail([1])'/>
<a data-bb-state='users.detail({id:1})'/>
<a data-bb-state href='/users/1'/>

The format for the data-bb-state value is 'statename([args]or{args})', where the args are passed to the callback as described in transitionMethod.

The first two examples are explicit state calls, but the third uses the url to infer the state (it actually calls goByUrl). The explicit triggers in the examples above will trigger the users.detail.transitionMethod callback. To use the inferred trigger, data-bb-state must be defined on the anchor and it must have an href url defined.

data-bb-options

You can add the data-bb-options attribute to your anchor to allow passing of the transitionOptions in the form of valid JSON.

Example Usage:

<a data-bb-state='users.detail([1])' data-bb-options='{"navigate": false}'/>

Additional Resources

##Contributors

  • PR's should only contain changes to .coffee files, the release js will be built later
  • Run gulp to autocompile coffeescript (both src and test/src) into /out for testing
  • Open test/test-runner.html to run the in-browser test suite, or run npm test for headless.

Change Log

2.0.4

  • Added new navigate event when a router.navigate is performed

2.0.3

  • Bugfix: goByUrl still uses go and gained a null in the params

2.0.2

  • Catch up deps

2.0.1

  • Bugfix: When calling go with an array of params, an odd search string gets appended to the url

2.0.0

  • Breaking: Add [transition/load] Success and Error events. Rename old bare events to [transition/load]Start

1.0.4

  • Bugfix: Transition is matching urls in a different priority than load

1.0.2

  • Bugfix: queryParams defined in state.url are dropped from the _urlAsTemplate

1.0.1

  • Bugfix: queryParams are getting dropped when transition navigate occurs

1.0

  • Add transitionOptions to go and goByUrl... supports navigate currently to allow pushState bypass
  • Support data-bb-options attribute to specify transitionOptions from anchor tags

0.2.4

  • Bugfix: go still requires args of some sort even if url contains no params
  • Bugfix: go breaks in ie8 if no arguments are provided

0.2.3

  • Bugfix: goByUrl dies if there is a state that doesn't have a url defined

0.2.2

  • Bugfix: Managers in wrong order after goByUrl runs

0.2.1

  • Bugfix: Managers are being traversed in the wrong order for goByUrl

0.2.0

  • Breaking: Move from conventional <a href>-to-state translation to direct url matching. This means that old conventional data-bb-state triggers for states without url definitions will no longer work.
  • Added Backbone.Manager.goByUrl to back data-bb-state href functionality

0.1.6

  • Breaking: Removed pre/post events... there was no guarantee of pre since it was async

0.1.5

  • Bugfix: Param matcher isn't excluding (

0.1.4

  • Bugfix: Use currentTarget instead of target for anchor state changes - @jmcnevin