In done-autorender 2.3.0 it’s now easy to separate your route data from the rest of your Application ViewModel. This means no more serialize: false
noise in your property definitions. This article will go over how you can upgrade your app to take advantage of this separation of concerns.
Routing changes in DoneJS
A pair of new features in DoneJS has made routing easier to implement. In CanJS we now have constructible can-components and can-value that makes it easier to route to the correct component without a lot of switch
statements in the template. The routing guide goes over this pattern as well as others (such as setting up routing rules within the ViewModel).
DoneJS apps use done-autorender which requires a slightly different way to do routing. We’re in the process of bringing as many of the new routing ideas from CanJS into DoneJS as we can.
route.data defaults to a DefineMap
In most apps the process of wiring up route params to the Application ViewModel has been straight-forward, but quite tedious. You typically need to define the params in two places like so:
const AppViewModel = DefineMap.extend("AppViewModel", {
page: "string",
id: "number"
});
route.register("{page}", { page: "home" });
route.register("{page}/{id}", { page: "product", id: 1 });
Notice that the above:
Requires defining two properties, page and id both in the DefineMap and in the routing rules.
The types are defined in the DefineMap but the defaults are defined in the routing rules.
Since all of the information is already contained within the routing rules we thought why not just make route.data be a DefineMap, with a page and id property defined.
If you upgrade to can-route 4.4.x that’s exactly what you get. In order to get the page you can simply get: route.data.page
.
This leads to a nice separation between your root ViewModel and the route data. In CanJS with automount you can define a Component like so:
Component.extend({
tag: "app-home",
ViewModel: {
routeData: {
default: () => {
route.register("{page}", { page: "home" });
route.start();
return route.data;
}
},
User: UserModel
},
view: `
<h1>Welcome to {{routeData.page}}</h1>
<h2>User {{user.name}}</h2>
`
});
This is much nicer. We don’t need to specify properties on the ViewModel to not be serialized to the URL, nor do we need to define a route observable type.
Specifying the route.data in done-autorender
The above change is nice, but in order to take advantage of it for DoneJS users we needed support in done-autorender. In done-autorender 2.3.0 it’s now possible to specify a property on your Application ViewModel to use as the route data, rather than using the ViewModel itself.
Specify the route-data attribute in your viewModel’s can-import statement like so:
<can-import from="todo-list/app" export-as="viewModel" route-data="routeData" />
The above says:
- Use the todo-list/app module as this template’s ViewModel.
1 Use the routeData property on that ViewModel to connect to route.data.
This property can of course be named whatever you wish, we are using routeData in documentation for clarity.
Defining your Application ViewModel just means setting it’s routeData property to the default route.data:
import DefineMap from "can-define/map/map";
import route from "can-route";
import RoutePushstate from "can-route-pushstate";
route.urlData = new RoutePushstate();
route.register("{page}", { page: "home" });
const AppViewModel = DefineMap.extend("AppViewModel", {
routeData: {
default: () => route.data
}
});
export default AppViewModel;
If you need to use properties within the route, such as the page in this case, just specify the routeData.page property. Here’s how that looks in your template:
<h1>Todo App</h1>
<h2>Page {{routeData.page}}</h2>
Upgrading your app
You can take advantage of these features simply:
- Upgrade to at least can-route 4.4.0 and done-autorender 2.3.0.
- Remove any properties from your AppViewModel that are specific to the route (anything that is defined in your routing rules).
- Add a property for your route data, we call it routeData. It should contain only a default export that is route.data.
- Update your index.stache and add the
route-data="routeData"
attribute to your viewModel’s can-import. - Change any references in the template, such as bindings and magic tags, to use
routeData.foo
instead of just foo. - The fun part: get rid of all of the
serialize: false
noise from your AppViewModel and never need to add it again.
That’s it! Even with a large app this change should take no more than an hour or so.
Looking ahead
Routing is an area that we continue to refine in DoneJS and CanJS. In DoneJS 3.0 we will bring over more of the patterns laid out in the Routing Guide. In the future we might unify the automount pattern with done-autorender somehow. The DoneJS Survey currently contains a proposal for simplifying routing configuration. We are always looking for feedback on how we can make routing easier, so please fill out the survey.