UNPKG

can

Version:

MIT-licensed, client-side, JavaScript framework that makes building rich web applications easy.

259 lines (178 loc) 7.86 kB
@function can.route can.route @group can.route.static static @inherits can.Map @download can/route @test can/route/test.html @parent canjs @group can.route.plugins plugins @link ../docco/route/route.html docco @description Manage browser history and client state by synchronizing the window.location.hash with a [can.Map]. @signature `can.route( template [, defaults] )` Create a route matching rule. @param {String} template the fragment identifier to match. The fragment identifier should start with either a character (a-Z) or colon (:). Examples: can.route(":foo") can.route("foo/:bar") @param {Object} [defaults] an object of default values @return {can.route} @body ## Use Watch this video for an overview of can.route's functionality and an example showing how to connect two tab widgets to the browser's history: <iframe width="662" height="372" src="https://www.youtube.com/embed/ef0LKDiaPZ0" frameborder="0" allowfullscreen></iframe> In the following CanJS community we also talk about web application routing: <iframe width="662" height="372" src="https://www.youtube.com/embed/0Hhuv5Qru9k" frameborder="0" allowfullscreen></iframe> ## Background Information To support the browser's back button and bookmarking in an Ajax application, most applications use the <code>window.location.hash</code>. By changing the hash (via a link or JavaScript), one is able to add to the browser's history without changing the page. This provides the basics needed to create history enabled Ajax websites. However, `can.route` addresses several other needs such as: - Pretty urls (actually hashes) - Keeping routes independent of application code - Listening to specific parts of the history changing - Setup / Teardown of widgets. ## How it works <code>can.route</code> is a [can.Map] that represents the <code>window.location.hash</code> as an object. For example, if the hash looks like: #!type=videos&id=5 the data in <code>can.route</code> looks like: { type: 'videos', id: 5 } `can.route` keeps the state of the hash in-sync with the `data` contained within `can.route`. ## can.Map `can.route` is a [can.Map]. Understanding `can.Map` is essential for using `can.route` correctly. You can listen to changes in an Observe with `bind(eventName, handler(ev, args...))` and change can.route's properties with [can.Map.prototype.attr attr]. ### Listening to changes in can.route Listen to changes in history by [can.Map.prototype.bind bind]ing to changes in <code>can.route</code> like: can.route.bind('change', function(ev, attr, how, newVal, oldVal) { }) - `attr` - the name of the changed attribute - `how` - the type of Observe change event (add, set or remove) - `newVal`/`oldVal` - the new and old values of the attribute ### Updating can.route Create changes in the route data with [can.Map.prototype.attr attr] like: can.route.attr('type','images'); Or change multiple properties at once like: can.route.attr({type: 'pages', id: 5}, true) When you make changes to can.route, they will automatically change the <code>hash</code>. ## Creating a Route Use <code>can.route(url, defaults)</code> to create a route. A route is a mapping from a url to an object (that is the can.route's state). In order to map to a specific properties in the url, prepend a colon to the name of the property like: can.route( "#!content/:type" ) If no routes are added, or no route is matched, can.route's data is updated with the [can.deparam deparamed] hash. location.hash = "#!type=videos"; // can.route -> {type : "videos"} Once routes are added and the hash changes, can.route looks for matching routes and uses them to update can.route's data. can.route( "#!content/:type" ); location.hash = "#!content/images"; // can.route -> {type : "images"} can.route.attr( "type", "songs" ) // location.hash -> "#!content/songs" Default values can be added to a route: can.route("content/:type",{type: "videos" }); location.hash = "#!content/" // can.route -> {type : "videos"} // location.hash -> "#!content/" Defaults can also be set on the root page of your app: can.route( "", { page: "index" } ); location.hash = "#!"; // can.route.attr() -> { page: "index" } // location.hash -> "#!" ## Initializing can.route After your application has created all of its routes, call [can.route.ready] to set can.route's data to match the current hash: can.route.ready() ## Changing the route. Typically, you don't set <code>location.hash</code> directly. Instead, you can change properties on <code>can.route</code> like: can.route.attr('type', 'videos') This will automatically look up the appropriate route and update the hash. Often, you want to create links. <code>can.route</code> provides the [can.route.link] and [can.route.url] helpers to make this easy: can.route.link("Videos", {type: 'videos'}) ## Demo The following demo shows the relationship between `window.location.hash`, routes given to `can.data`, `can.route`'s data, and events on `can.data`. Most properties are editable so experiment! @iframe can/route/demo.html 980 ## IE Compatibility Internet Explorer 6 and 7 does not support `window.onhashchange`. Even Internet Explorer 8 running in IE7 compatibility mode reports `true` for `onhashchange` in window, even though the event isn't supported. If you are using jQuery, you can include Ben Alman's [HashChange Plugin](http://benalman.com/projects/jquery-hashchange-plugin/) to support the event in the unsupported browser(s). ## Using routes with `can.Control` Using templated event handlers, it is possible to listen to changes to `can.route` within `can.Control`. This is convenient as it allows the control to listen to and make changes whenever the route is modified, even outside of the control itself. // create the route can.route("#!content/:type") can.Control({ // the route has changed "{can.route} change": function(ev, attr, how, newVal, oldVal) { if (attr === "type") { // the route has a type } } }); ### Creating and binding routes with `can.Control.route` Using [can.Control.route], a builtin plugin to CanJS, cuts down on the amount of code needed to work with `can.route` in `can.Control`. With this plugin, it is possible to both create routes and bind to `can.route` at the same time. Instead of creating several routes to handle changes to __type__ and __id__, write something like this in a control: can.Control({ // the route is empty "route": function(data) { }, // the route has a type ":type route": function(data) { }, // the route has a type and id ":type/:id route": function(data) { } }); ### Getting more specific with the `can.Map.delegate` plugin Sometimes, you might only want to trigger a function when the route changes only once, even if the route change gets called multiple times. By using the [can.Map.delegate] plugin, this is extremely easy. This plugin allows you to listen to change, set, add, and remove on `can.route`. If you wanted to, say, show a list of recipes when __type__ was set to recipe and show a specific recipe when __id__ was set, you could do something like: can.Control({ "{can.route} type=recipe set": function( ev, prop, how, newVal, oldVal ) { // show list of recipes }, "recipe/:id": function(data) { // show a single recipe } }); If we didn't only listen to when recipe is set, then every time we chose to show a single recipe, we would create and show the list of recipes again which would not be very efficient.