Multiple Todolists

An example of using falcon with a basic, single view, todo list

by stoodder

HTML

<script src="http://code.jquery.com/jquery-1.10.2.min.js"></script>
<link rel="stylesheet" href="http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/css/bootstrap-combined.min.css">
<script src="http://knockoutjs.com/downloads/knockout-3.0.0.js"></script>
<script src="http://stoodder.github.io/falconjs/scripts/falcon.min.js"></script>
<div id="application"></div>


<template id="layout_tmpl">
    <div id="layout-sidebar">
        <button data-bind="click: $view.newTodoList">New Todo List</button>
        <ul id="todolist-list" data-bind="foreach: $view.todo_lists">
            <li data-bind="click: $view.selectTodoList, css: {'selected': $view.isSelectedList( $data )}">
                <strong data-bind="text: title"></strong>
                <p data-bind="text: description"></p>
            </li>
        </ul>
    </div>
    <div id="content-view">
        <!-- ko ifnot: $view.current_view -->
            <div class="alert alert-info">
                <h4>No List Selected</h4>
                <p>Start by selecting a todo list from the menu on the left.</p>
            </div>
        <!-- /ko -->
        <!-- ko if: $view.current_view -->
            <!-- ko view: $view.current_view --><!-- /ko -->
        <!-- /ko -->
    </div>
</template>



<template id="todo_list_form_tmpl">
    <form class="form-horizontal" data-bind="submit: $view.saveTodoList">
        <div class="control-group">
            <label class="control-label">Title</label>
            <div class="controls">
                <input type="text" class="input-large" data-bind="value: $view.title" />
            </div>
        </div>
        <div class="control-group">
            <label class="control-label">Description</label>
            <div class="controls">
                <textarea data-bind="value: $view.description"></textarea>
            </div>
        </div>
        <div class="form-actions">
            <input type="button" class="btn" value="Cancel" data-bind="click:...

CSS

a {
    cursor: pointer;
}

a:hover {
    text-decoration: underline;
}

#application {
    position: absolute;
    left: 0px;
    height: 100%;
    width: 100%;
}

#layout-sidebar {
    position: absolute;
    top: 0px;
    left: 0px;
    height: 100%;
    width: 175px;
    overflow: auto;
    background-color: #f4f4f4;
}

ul#todolist-list {
    padding: 0px;
    margin: 0px;
    color: #333333;
}

ul#todolist-list li:hover {
    background-color: #e8e8e8;
}

ul#todolist-list li {
    padding: 10px 5px;
    cursor: pointer;
}

ul#todolist-list li.selected {
    background-color: #333333;
    color: #f4f4f4;
}

ul#todolist-list li strong {
    font-size: 14px;
    font-weight: bold;
}

ul#todolist-list li p {
    font-size: 11px;
    font-style: italic;
}

#content-view {
    position: absolute;
    left: 175px;
    top: 0px;
    bottom: 0px;
    right: 0px;
    overflow: auto;
    padding: 10px;
}

.todo-list {
    padding: 0px;
    margin: 0px;
    list-style: none;
}

.todo-list li.completed {
    color: grey;
    text-decoration: line-through;
}

JavaScript

// The Todo model hasn't changed
var Todo = Falcon.Model.extend({
    url: 'todo',
    
    observables: {
        'text': '',
        'is_complete': ''
    }
});

// The Todos collection hasn't changed
var Todos = Falcon.Collection.extend({
    model: Todo
});

// Here we're adding a TodoList model to represent a specific list 
// of todos that also has a list title and description.
var TodoList = Falcon.Model.extend({
    url: 'todo_list',
    
    defaults: {
        // Here we're saying that the 'todos' attribute of a TodoList will be a
        // Todos collection who's parent is this TodoList (hence the 'this' 
        // that being passed into the constrcutor). A model or collection's
        // parent object is used for generating RESTful urls in the makeUrl 
        // function that will be used in any sync() related method.
        // This concept will be discussed more in the following tutorial
        'todos': function() { return new Todos( this ); }
    },
    
    observables: {
        'title': '',
        'description': ''
    }
});

var TodoLists = Falcon.Collection.extend({
    model: TodoList 
});




// Here we're definine a View to display a single todo list, it's title
// and description, and any of it's todos
var TodoListView = Falcon.View.extend({
    url: '#todo_list_tmpl',
    
    // If a function is defined for a specific default, that function is 
    // called with the same arguments passed into the constructor of this 
    // class. Hence, in the example of todo_list, a TodoList is passed into 
    // the TodoListView constrcutor, which then initializes all of the 
    // defaults including the todo_list. Since the todo_list default simply 
    // returns the todo_list that was passed into it, that value is assigned 
    // to the todo_list attribute.  Note: This is the same as writting
    // "this.todo_list = todo_list;" in the 'initialize' method below.
    defaults: {
        'todo_list': function( todo_list ){ return todo_list;...