Routing page

Set up routing (can-route)

Make it so that the following URLs display the corresponding todos:

  • #! or - All todos
  • #!active - Only the incomplete todos
  • #!complete - Only the completed todos

Also, the All, Active, and Completed buttons should link to those pages and a class="selected" property should be added if they represent the current page.

What you need to know

  • route is used to connect a DefineMap’s properties to the URL. This is done with route.data like:

    route.data = new DefineMap();
    
  • route can create pretty routing rules. For example, if #!login should set the page property of the AppViewModel to "login", use route.register() like:

    route.register("{page}");
    
  • route.start() initializes the connection between the URL and the AppViewModel. After you’ve created all your application’s pretty routing rules, call it like:

    route.start()
    
  • The can-stache-route-helpers module provides helpers that use route.

    routeCurrent returns truthy if the current route matches its first parameters properties.

    {{# if(routeCurrent(page='login',true)) }}
      You are on the login page.
    {{/ if }}
    

    routeUrl returns a URL that will set its first parameters properties:

    <a href="{{ routeUrl(page='login') }}">Login</a>
    

The solution

Click to see the solution

Update index.js to the following:

// index.js
import {Component, route, DefineMap} from "can";
import view from "./index.stache";
import Todo from "~/models/todo";
import "~/models/todos-fixture";
import test from "can-todomvc-test";


route.register("{filter}");

Component.extend({
    tag: "todo-mvc",
    view,
    ViewModel: {
        appName: {default: "TodoMVC"},
        routeData: {
            default(){
                route.start();
                return route.data;
            }
        },
        allTodos: {
            get: function(lastSet, resolve) {
                Todo.getList({}).then(resolve);
            }
        },
        get todosList() {
            if(this.allTodos) {
                if(this.routeData.filter === "complete") {
                    return this.allTodos.complete;
                } else if(this.routeData.filter === "active") {
                    return this.allTodos.active;
                } else {
                    return this.allTodos;
                }
            }
        },
        get allChecked() {
            return this.todosList && this.todosList.allComplete;
        },
        set allChecked(newVal) {
            this.todosList && this.todosList.updateCompleteTo(newVal);
        }
    }
});

const appVM = window.appVM = document.querySelector("todo-mvc").viewModel;

test(appVM);

Update index.stache to the following:

<!-- index.stache -->
<can-import from="~/components/todo-create/" />
<can-import from="~/components/todo-list/" />
<can-import from="can-stache-route-helpers" />
<section id="todoapp">
    <header id="header">
        <h1>{{ this.appName }}</h1>
        <todo-create/>
    </header>
    <section id="main" class="">
        <input id="toggle-all" type="checkbox"
          checked:bind="this.allChecked"
          disabled:from="this.todosList.saving.length" />
        <label for="toggle-all">Mark all as complete</label>
        <todo-list todos:from="this.todosList" />
    </section>
    <footer id="footer" class="">
        <span id="todo-count">
            <strong>{{ allTodos.active.length }}</strong> items left
        </span>
        <ul id="filters">
            <li>
                <a href="{{ routeUrl(filter=undefined) }}"
                    {{# routeCurrent(filter=undefined) }}class='selected'{{/ routeCurrent }}>
                    All
                </a>
            </li>
            <li>
                <a href="{{ routeUrl(filter='active') }}"
                    {{# routeCurrent(filter='active') }}class='selected'{{/ routeCurrent }}>
                    Active
                </a>
            </li>
            <li>
                <a href="{{ routeUrl(filter='complete') }}"
                    {{# routeCurrent(filter='complete') }}class='selected'{{/ routeCurrent }}>
                    Completed
                </a>
            </li>
        </ul>
        <button id="clear-completed"
            on:click="allTodos.destroyComplete()">
            Clear completed ({{ allTodos.complete.length }})
        </button>
    </footer>
</section>

Success! You’ve completed this guide. Have questions or comments? Join our Discord and let us know in the #canjs channel or our forums!