---
title: Hashchange Routing with can.route - Part 2 - Advanced
description: Hashchange Routing with can.route - Part 2 - Advanced
image: https://www.bitovi.com/hubfs/canjs-background-blue-1.png
---

- ![AI implementation](https://www.bitovi.com/hubfs/AIConsultingIcon.svg)
  
  [AI implementation](https://www.bitovi.com/services/ai-consulting)
- ![Systems engineering](https://www.bitovi.com/hubfs/icon%20-%20backend.svg)
  
  [Systems engineering](https://www.bitovi.com/services/systems-engineering-consulting)
- ![Project Management](https://www.bitovi.com/hubfs/icon%20-%20PM.svg)
  
  [Project Management](https://www.bitovi.com/services/agile-project-management-consulting)
- ![Product Design](https://www.bitovi.com/hubfs/icon%20-%20design.svg)
  
  [Product Design](https://www.bitovi.com/services/product-design-consulting)
- ![Frontend development](https://www.bitovi.com/hubfs/icon%20-%20frontend.svg)
  
  [Frontend development](https://www.bitovi.com/services/frontend-development-consulting)
- [View more
  
  →
  
  ](https://www.bitovi.com/digital-consulting-services)

We're Experts in...

- [JavaScript](https://www.bitovi.com/services/frontend/javascript-consulting)
- [AI training](https://www.bitovi.com/ai-training-for-software-engineers)
- [Angular](https://www.bitovi.com/services/frontend/angular-consulting)
- [Design systems](https://www.bitovi.com/services/axure-figma-migration)
- [React](https://www.bitovi.com/services/frontend/react-consulting)
- [Temporal](https://www.bitovi.com/services/backend/temporal-consulting)
- [React Native](https://www.bitovi.com/services/frontend/react-consulting/react-native)
- [Node.js](https://www.bitovi.com/services/backend/nodejs-consulting)

Showcase

![Yum! Brands](https://www.bitovi.com/hubfs/yum-showcase-link-1.png)

[View case study](https://www.bitovi.com/en/bitovi-yum-case-study)

More Projects

- [![Levi's](https://www.bitovi.com/hubfs/levis.svg)](https://www.bitovi.com/web-application-consulting-work/levis-ecommerce-responsive-redesign)
- [![Christie's International Real Estate](https://www.bitovi.com/hubfs/christies.svg)](https://design.bitovi.com/christies)
- [![BAFS](https://www.bitovi.com/hubfs/bafs.svg)](https://www.bitovi.com/ux-design-consulting/ux-case-studies/bafs-ppp)
- [View more
  
  →
  
  ](https://www.bitovi.com/our-software-consulting-work)

Open Source Tools

We build powerful tools and open source them to support the community.

[See what we've built →](https://www.bitovi.com/open-source)

- [![Blog](https://www.bitovi.com/hubfs/icon%20-%20blog.svg)
  
  BlogWe post about delivering products and solving problems.
  
  ](https://www.bitovi.com/blog)
- [![Partnerships](https://www.bitovi.com/hubfs/Handshake-1.svg)
  
  PartnershipsLearn about Bitovi's technology partners
  
  ](https://www.bitovi.com/partnerships)
- [![Academy](https://www.bitovi.com/hubfs/icon%20-%20academy%20(4).svg)
  
  AcademyFree courses to build delivery skills
  
  ](https://www.bitovi.com/academy)
- [![Open source tools](https://www.bitovi.com/hubfs/icon%20-%20open%20source.svg)
  
  Open source toolsUse or contribute to our community
  
  ](https://www.bitovi.com/open-source)

Let's Connect

- [![Discord](https://www.bitovi.com/hubfs/DiscordLogo.svg)
  
  Discord
  
  ](https://discord.gg/J7ejFsZnJ4)
- [![LinkedIn](https://www.bitovi.com/hubfs/LinkedinLogo.svg)
  
  LinkedIn
  
  ](https://www.linkedin.com/company/bitovi/)
- [![GitHub](https://www.bitovi.com/hubfs/GithubLogo.svg)
  
  GitHub
  
  ](https://github.com/bitovi/)

![Eggbot](https://www.bitovi.com/hubfs/build_assets/bitovi-limbo-cms-react/338/js_client_assets/assets/eggbot-LTGhdSGL.png)

Name *

Work Email *

Phone

What's your project?

Send

### Contact Us

(312) 620-0386contact@bitovi.com

 May 26, 2012

# Hashchange Routing with can.route - Part 2 - Advanced

 Hashchange Routing with can.route - Part 2 - Advanced

![Justin Meyer](https://www.bitovi.com/hubfs/justin.jpeg)

 Justin Meyer

Share:

[![Twitter](https://www.bitovi.com/hubfs/limbo-generated/_astro/twitter-white.os3xLc3C_Z2nW4or.svg) ](https://twitter.com/intent/tweet?text=) [![Reddit](https://www.bitovi.com/hubfs/limbo-generated/imgs/icons/reddit.png) ](http://reddit.com/submit?url=)

This article is the second of a two part series ([part1](http://bitovi.com/blog/2012/05/hashchange-routing-can-route-1.html)) on [CanJS's](http://canjs.us) hashchange routing system - [can.route](http://canjs.us#can_route). This part walks through through `can.route`'s advanced functionality. Specifically, it covers:

- The observeable nature of `can.route`
- Defining routes
- Making controls operate independently
- Using the delegate plugin for setup and teardown

But first a little motivation!

## Motivation - got to keep em seperated

If you're building big apps, the ability to break up an application into discrete parts that know little about each other as possible should be a big concern to you. For small apps, you can probably stick with techniques in [part 1](http://bitovi.com/blog/2012/05/hashchange-routing-can-route-1.html).

In big apps, you want to keep routing separate from your controls. For example, in Ruby on Rails, you have a list of defined routes like:

```
match '/group/:name', :controller => "group", :action => "name"
match ':controller/:action/:id/:user_id'
```

Rails matches incoming requests against these routes and calls a particular controller and method. This is great because routes can change independently of controller code.

In most JavaScript libraries (likely all others but CanJS), this kind of thing is not possible or difficult. There is often a strong association between the route path and your code. You can see this in the following examples:

```
// SammyJS
this.get('#/search/:query', function(context) {
   ...
});

// Backbone
var Workspace = Backbone.Router.extend({
  routes: {   
    "search/:query": "search",   
  },
  search: function(query, page) { ... }
});

// CanJS
var Workspace = can.Control({
  "search/:query route" : function(){}
})
```

You'll notice that it would be difficult to maintain routes independent of the code that runs them. To do this, you'd have to do something like:

```
window.ROUTES = {
  query: '#/search/:query'
}

// SammyJS
this.get(ROUTES.query, function(context) {
  ...
});

// Backbone - I'm not sure?

// CanJS
var Workspace = can.Control({
  "help route" : function(){},
  "{ROUTES.query} route" : function(){}
})
```

You might be asking yourself:

> Why don't JS libraries do it more like Ruby on Rails?

**Answer:** The lifecycle of a page request on the server is completely different than a JS app's lifecycle. On the server, you have one input, an http request, and one output, the html (typically) response.

On the client, one event can trigger many different changes. Going from `#!customer/4` to `#!tasks/7` might mean:

- Updating the navigation
- Replacing the list of recipes with a list of tasks
- Changing a "details" panel to show task 7

Ideally all of these items should know as little as possible about each other. It's even better if you don't have to create a managing controller that knows about all of them.

With CanJS, you can distribute routing functionality.

## Observable routes

To help make breaking up routing easy, `can.route` is a special [can.Observe](http://canjs.us/#can_observe), one that is cross-bound to hash. When the hash changes, the route changes. When the route changes, the hash changes. This lets you:

Listen to changes in a specific property like:

```
can.route.bind("type", function(ev, newVal, oldVal){

})
```

Or all properties at once like:

```
can.route.bind("change", function(ev, attr, how, newVal, oldVal){

})
```

Change a single property like:

```
can.route.attr("type","todo")
```

Change multiple properties like:

```
can.route.attr({
  type : "task",
  id: 5
})
```

The observable nature of **can.route** is particularly useful for allowing widgets to work independently of each other. This, like most things, is easier understood with an example.

Consider a history-enabled tabs widget where multiple tabs can be present on the page at once like the following:

JSFiddle

Each `HistoryTab` is configured with the route attribute it's listening to like:

```
new HistoryTabs( '#components',{attr: 'component'});
new HistoryTabs( '#people',{attr: 'person'});
```

`HistoryTab` uses this to listen to changes in that attribute, activate and show the new tab with:

```
"{can.route} {attr}" : function( route, ev, newVal, oldVal ) {
  this.activate(newVal, oldVal)
}
```

The routes are defined with:

```
can.route(":component",{
  component: "model",
  person: "mihael"
});

can.route(":component/:person",{
  component: "model",
  person: "mihael"
});
```

Learn more about the example on the [CanJS Recipes page](http://canjs.us/recipes.html#routing-history_tabs).

The important thing to understand from this example is that:

- Many `HistoryTabs` can be created and work independently
- `HistoryTabs` only cares about the data the hash represents, it is not aware of the defined routes.

This last point is especially important for the next section.

## Defining routes

`can.route( route, defaults )` is used to create routes that update `can.route`'s attributes. For example:

```
can.route(":type",{ type: "recipes" })
```

`route` is a **parameterized** url hash to match against. Specify parameterized url parts with `:PARAM_NAME` like `"recipes/:recipeId"`. If the hash matches the `route`, it sets the route's attributes values to the parameterized part. For example:

```
can.route("recipes/:recipeId");
window.location.hash = "!recipes/5";
can.route.attr('recipeId') //-> "5"
```

`defaults` is an object of attribute-value pairs that specify default values if the `route` is matched but a parameterized value is missing or not provided. The following shows a default value filling in a missing parameterized value:

```
can.route(":type",{ type: "recipes" })
window.location.hash = ""
can.route.attr("type") //-> "recipes"
```

The following shows defaults being used as extra values when a route is matched:

```
can.route("tasks/:id",{type: "tasks"})
window.location.hash = "!tasks/5"
can.route.attr("type") //-> "tasks"
can.route.attr("id")   //-> "5"
```

Using `can.route` on HistoryTabs, we can specify a pretty url and default tabs to select with the following routes:

```
can.route(":component",{
  component: "model",
  person: "mihael"
});

can.route(":component/:person",{
  component: "model",
  person: "mihael"
});
```

This sets up the following behavior:

| selected component value | selected person value | example hash |
| --- | --- | --- |
| model | mihael | "#!" (empty hash) |
| model or view | mihael | "#!model" |
| model | brian or justin | "#!/brian" |
| model or view | brian or justin | "#!model/brian" |

## Routing Independence

The next example shows a history-enabled issues page. The user can filter issues and select an individual issue to get more information from.

JSFiddle

The example shows how separate widgets can respond to overlapping route properties. It creates separate `Nav`, `Issues`, and `Details` controls that respond to `filter` and `id` attributes.

`Nav` responds to `filter`. `Issues` responds to `filter` and `id`. `Details` responds to `id`.

Read more about this example on [CanJS's recipes page](http://canjs.us/recipes.html#routing-observe_backed_routes).

## Setup and Teardown

The [Observe delegate plugin](http://canjs.us/#plugins-can_observe_delegate) can add even more power to routes. With it, you can listen to more specific observe changes.

For example, you can listen to when the `type` property is `"issues"` like:

```
can.route.delegate("type=issues","set",function(){
  // CODE
})
```

Within a can.Control, this looks like

```
"{can.route} type=issues set" : function(){
  // code
}
```

You can also listen to when a value is added, set, or removed from `can.route` like:

```
"{can.route} module add" : function(){
   // show modules
},
"{can.route} module set" : function(){
  // highlight selected module
},
"{can.route} module remove" : function(){
   // remove modules
}
```

This is used in the following example to show modules when a module is selected (by clicking "Login") and remove them when module is unset (by clicking "Logout").

JSFiddle

## Conclusion

[can.route](http://canjs.us#can_route) is not an ordinary url-matching routing helper. It understand that routes reflect the state of an application and allows that state to be represented by an object, letting the developer listen to specific property changes on that object. It does all this, while still providing the basic syntax covered in [part1](http://bitovi.com/blog/2012/05/hashchange-routing-can-route-1.html).

`can.route` reflects the duel nature of CanJS - start easy then scale.

[![Tag for open source](https://www.bitovi.com/hubfs/limbo/icons/tag.svg) open source ](https://www.bitovi.com/blog/topic/open-source)[![Tag for development](https://www.bitovi.com/hubfs/limbo/icons/tag.svg) development ](https://www.bitovi.com/blog/topic/development)

 Previous Post

![canjs-background-blue-1](https://www.bitovi.com/hs-fs/hubfs/canjs-background-blue-1.png?height=117&name=canjs-background-blue-1.png) [ Hashchange Routing with can.route - Part 1 - Basics ](https://www.bitovi.com/blog/hashchange-routing-with-can-route-part-1-basics)

  

 Next Post

![Bitovi Hoops for Heart with the American Stroke Association](https://www.bitovi.com/hs-fs/hubfs/Basketball-on-Court2.jpg?height=117&name=Basketball-on-Court2.jpg) [ Bitovi Hoops for Heart with the American Stroke Association ](https://www.bitovi.com/blog/bitovi-hoops-for-heart-with-the-american-stroke-association)

```json
{
  "@context" : "http://schema.org",
  "@type" : "Organization",
  "address" : {
    "@type" : "PostalAddress",
    "addressCountry" : "United States",
    "addressLocality" : "Libertyville",
    "addressRegion" : "IL",
    "postalCode" : "60048",
    "streetAddress" : "1134 Pine Tree Lane "
  },
  "alternateName" : "Bitovi",
  "areaServed" : {
    "@type" : "GeoCircle",
    "geoMidpoint" : {
      "@type" : "GeoCoordinates",
      "latitude" : "41.8781",
      "longitude" : "87.6298"
    },
    "geoRadius" : "5000 km"
  },
  "description" : "Bitovi is a UX, UI design and front-end JavaScript development consulting company",
  "email" : "contact@bitovi.com",
  "image" : "https://www.bitovi.com/hubfs/bitovi-logo-x2.png",
  "logo" : "https://www.bitovi.com/hubfs/bitovi-logo-23-1.svg",
  "mainEntityOfPage" : {
    "@id" : "https://www.bitovi.com/blog/hashchange-routing-with-can-route-part-2-advanced",
    "@type" : "WebPage",
    "description" : "Hashchange Routing with can.route - Part 2 - Advanced"
  },
  "naics" : "541511",
  "name" : "Bitovi Web App Consulting",
  "sameAs" : [ "https://www.facebook.com/BitoviLLC/", "https://twitter.com/bitovi", "https://www.linkedin.com/company/bitovi" ],
  "telephone" : "312-620-0386",
  "url" : "http://bitovi.com"
}
```

```json
{
  "@context" : "http://schema.org",
  "@type" : "BlogPosting",
  "author" : {
    "@type" : "Person",
    "name" : "Justin Meyer"
  },
  "dateModified" : "November 29, 2018, 6:46:00 PM",
  "datePublished" : "2012-05-26 20:26:37",
  "description" : "Hashchange Routing with can.route - Part 2 - Advanced",
  "headline" : "Hashchange Routing with can.route - Part 2 - Advanced",
  "image" : {
    "@type" : "ImageObject",
    "url" : "https://www.bitovi.com/hubfs/canjs-background-blue-1.png"
  },
  "publisher" : {
    "@type" : "Organization",
    "logo" : {
      "@type" : "ImageObject",
      "url" : "https://www.bitovi.com/hubfs/bitovi-logo-23-1.svg"
    },
    "name" : "Bitovi"
  }
}
```