Skip to content

Commit

Permalink
Fixing documentation issues (#211)
Browse files Browse the repository at this point in the history
* Updated examples to use codepen and indentation.

* Moved documentation from inline to seperate files.

This includes the following documentation
- deparam
- isCurrent
- link
- param
- url

All documentation is avaliable in the doc folder.

* removed newline split from start.md

* Updated examples and linked to Codepen.

- currentRule.md
- deparam.md
- isCurrent.md
- link.md
- param.md
- url.md

* updated deparam documentation and example.

* Incremental Changes to Examples and Docs

`doc/currentRule.md` needed major updates to the signature example so it
would work. This includes starting the route and adding Timeouts.

Other changes are minor refactoring to improve over all consistency.

* Updated examples and documentation for can-route and data. Fixed spacing in link and url.

* minor doc and example updates.

This includes removing examples that have been deemed redundant.

* Deprecated route.link

- There were some issues creating a deprecated group in can-route.md. Added to #209.

* Added example to show possible use for using route.stop.

* Minor documentation updates and examples

* added note about how route.rule is used.

* removed incorrect doc from rule.md

* Small documentation updates

* using Safari 11 in SauceLabs
  • Loading branch information
indifferentghost authored Nov 8, 2018
1 parent 8838352 commit 8554cf5
Show file tree
Hide file tree
Showing 16 changed files with 688 additions and 557 deletions.
325 changes: 206 additions & 119 deletions doc/can-route.md

Large diffs are not rendered by default.

30 changes: 19 additions & 11 deletions doc/currentRule.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,26 @@
@description A compute representing the currently matched routing rule route.

@signature `route.currentRule()`
@return {String} The currently matched [can-route.register registered] routing rule.
Use `route.currentRule()` to find the current route rule.

@body
```js
import {route} from "can";

## Use
route.register( "{type}" );
route.register( "{type}/{subtype}" );
route.start();

Use `route.currentRule()` to find the current route rule.
route.data.type = "foo";
setTimeout(() => {
console.log( route.currentRule() ); //-> "{type}"

route.data.subtype = "bar";
}, 100);

```js
route.register( "{type}", { type: "foo" } );
route.register( "{type}/{subtype}" );
route.currentRule(); // "{type}"
route.data.subtype = "foo";
route.currentRule(); // "{type}/{subtype}"
```
setTimeout(() => {
console.log( route.currentRule() ); //-> "{type}/{subtype}"
}, 200);
```
@codepen

@return {String} The currently matched [can-route.register registered] routing rule.
182 changes: 35 additions & 147 deletions doc/data.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,167 +5,55 @@ An observable key-value object used to cross bind to the url observable [can-rou

@type {Object} If `route.data` is set to a [can-reflect]ed observable object of
key-value pairs, once [can-route.start] is called, changes in `route.data`'s
properties will update the hash and vice-versa.
properties will update the hash and vice-versa. `route.data` defaults to a [can-define/map/map].

```js
import DefineMap from "can-define/map/map";
import route from "can-route";
```html
<mock-url></mock-url>
<script type="module">
import {DefineMap, route} from "can";
import "//unpkg.com/mock-url@^5.0.0/mock-url.mjs";
route.data = new DefineMap( { page: "" } );
route.register( "{page}" );
route.start();
```
route.data = new DefineMap( {page: ""} );
route.register( "{page}" );
route.start();
location.hash = "#!example";
setTimeout(()=> {
console.log( route.data ); //-> {page: "example"}
}, 100);
</script>
```
@codepen


@type {HTMLElement} If `route.data` is set to an element, its
observable [can-view-model] will be used as the observable connected
to the browser's hash.
to the browser's hash.

```js
import Component from "can-component";
import route from "can-route";

Component.extend( {
tag: "my-app",
autoMount: true,
ViewModel: { /* ... */ },
view: { /* ... */ }
} );
route.data = document.querySelector( "my-app" );
route.register( "{page}" );
route.start();
```
<section class="warnings">
<div class="deprecated warning">
<h3>Deprecated</h3>
<div class="signature-wrapper">
<p>Assigning an <a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement">HTMLElement</a> to <code>route.data</code> has been deprecated in favor of setting it to an observable. If you have any further questions please refer to the [guides/routing Routing] guide.
</div>
</div>
</section>

@body

## Background

One of the biggest challenges in a complex application is getting all the different parts of the app to talk to each other simply, cleanly, and reliably.

An elegant way to solve this problem is using the [Observer Pattern](http://en.wikipedia.org/wiki/Observer_pattern). A single object, which can be called [Application ViewModel](https://www.youtube.com/watch?v=LrzK4exG5Ss), holds the high level state of the application.
For in-depth examples see the the [guides/routing Routing] guide.

## Use

Setting `route.data` is an easy way to cross-bind your Application ViewModel object to `route`. This will serialize your Application ViewModel into the hash (or pushstate URLs).

```js
const ViewModel = DefineMap.extend( {
petType: "string",
storeId: "number"
} );
const viewModel = new ViewModel( {
petType: "string",
storeId: "number"
} );
route.data = viewModel;
```

`route.data` can also be set to a constructor function. A new instance will be created and bound to:

```js
const ViewModel = DefineMap.extend( {
page: {
type: "string",
set: function( page ) {
if ( page === "user" ) {
this.verifyLoggedIn();
}
return page;
}
}
} );
route.data = ViewModel;
```

## When to set it

Set `route.data` at the start of the application lifecycle, before any calls to `route.addEventListener`. This will allow events to correctly bind on this new object.

## Demo

The following shows creating an Application ViewModel that loads data at page load, has a virtual property 'locationIds' which serializes an array, and synchronizes the viewModel to can-route:

@demo demos/can-route/data.html

## Complete example

The following example shows loading some metadata on page load, which must be loaded as part of the Application ViewModel before the components can be initialized

It also shows an example of a "virtual" property on the AppViewModel called locationIds, which is the serialized version of a non-serializeable can.List called locations. A setter is defined on locationIds, which will translate changes in locationIds back to the locations can.List.
`route.data` defaults to [can-define/map/map], but `route.data` can be set to any observable. The following uses [can-observe]:

```js
const Location = DefineMap.extend( {
selected: "boolean",
id: "any"
} );
const LocationList = DefineList.extend( {
"*": Location
} );
const AppViewModel = DefineMap.extend( {
locations: {
type: "any",

// don't serialize this property at all in the route
serialize: false
},

// virtual property that contains a comma separated list of ids
// based on locations that are selected
locationIds: {

// comma separated list of ids
serialize: function() {
const selected = thislocations.filter(
function( location ) {
return location.selected;
} );
const ids = [];
selected.each( function( item ) {
ids.push( item.id );
} );
return selected.join( "," );
},
import {DefineMap, route, observe} from "can/everything";

// toggle selected from a comma separated list of ids
set: function( val ) {
let arr = val;
if ( typeof val === "string" ) {
arr = val.split( "," );
}

// for each id, toggle any matched location
this.locations.forEach( function( location ) {
if ( arr.indexOf( location.id ) !== -1 ) {
location.selected = true;
} else {
location.selected = false;
}
} );
}
}
} );

// initialize and set route.data first, so anything binding to can-route
// will work correctly
const viewModel = new AppViewModel();
route.data = appViewModel;

// GET /locations
const locations = new Location.List( {} );

// when the data is ready, set the locations property
locations.done( function() {
viewModel.locations = locations;

// call start after the AppViewModel is fully initialized
route.start();
} );
route.data = new observe();
route.register( "{page}", { page: "home" } );
route.start();
console.log( route.data.page ) //-> "home"
```

## Why

The Application ViewModel object, which is cross-bound to the can-route via `route.data` and represents the overall state of the application, has several obvious uses:

* It is passed into the various components and used to communicate their own internal state.
* It provides deep linking and back button support. As the URL changes, Application ViewModel changes cause changes in application components.
* It provides the ability to "save" the current state of the page, by serializing the Application ViewModel object and saving it on the backend, then restoring with that object to load this saved state.
@codepen
69 changes: 69 additions & 0 deletions doc/deparam.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
@function can-route.deparam deparam
@parent can-route.static

@description Extract data from a route path.

@signature `route.deparam(url)`

Extract data from a url fragment, creating an object representing its values. The url fragment could be a [location.hash](https://developer.mozilla.org/en-US/docs/Web/API/HTMLHyperlinkElementUtils/hash) or [location.search](https://developer.mozilla.org/en-US/docs/Web/API/HTMLHyperlinkElementUtils/search).

```js
import {route} from "can";

const result = route.deparam("page=home");
console.log( result.page ); //-> "home"
```
@codepen

@param {String} url A route fragment to extract data from.
@return {Object} An object containing the extracted data.

@body

## Use

`route.deparam` creates a data object based on the query string passed into it. This is useful to create an object based on the `location.hash`.

```js
import {route} from "can";

const result = route.deparam("id=5&type=videos");
console.log( result ); //-> { id: 5, type: "videos" }
```
@codepen

It's important to make sure the hash or exclamation point is not passed to `route.deparam` otherwise it will be included as a property.

```html
<mock-url></mock-url>
<script type="module">
import "//unpkg.com/mock-url@^5.0.0";
import {route} from "can";
route.data = {};
route.register("")
route.data.id = 5; // location.hash -> #!id=5
route.data.type = "videos"; // location.hash -> #!&id=5&type=videos
route.start();
// setting datatype is synchronous
setTimeout(() => {
const result = route.deparam(location.hash);
console.log( result ); //-> { #!: "", id: "5", type: "videos" }
}, 300);
</script>
```
@codepen

`route.deparam` will try and find a matching route and, if it does, will deconstruct the URL and parse out the key/value parameters into the data object.

```js
import {route} from "can";

route.register("{type}/{id}");

const result = route.deparam("videos/5");
console.log( result ); //-> { id: 5, type: "videos" }
```
@codepen
34 changes: 34 additions & 0 deletions doc/isCurrent.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
@function can-route.isCurrent isCurrent
@parent can-route.static

@description Check if data represents the current route.

@signature `route.isCurrent(data [,subsetMatch] )`

Compares `data` to the current route. Used to verify if an object is
representative of the current route.

The following example calls `route.isCurrent` with a single matching parameter when `route.data` has two properties. If `subsetMatch` is `false` or left default `route.isCurrent` won't try and match subsets.

```js
import {route} from "can";

route.data = {page: "recipes", id: "5"}; // location.hash -> "#!&page=recipes&id=5"
route.start();

setTimeout(() => {
const completeSet = route.isCurrent( {page: "recipes"} );
console.log( completeSet ); //-> false

const subSet = route.isCurrent( {page: "recipes"}, true );
console.log( subSet ); //-> true
}, 200);
```
@codepen

@param {Object} data Data to check against the current route.
@param {Boolean} [subsetMatch] If true, `route.current` will return true
if every value in `data` matches the current route data, even if
the route data has additional properties that are not matched. Defaults to `false`
where every property needs to be present.
@return {Boolean} Whether the data matches the current URL.
Loading

0 comments on commit 8554cf5

Please sign in to comment.