Todo Knockout Sample

The base Todo sample demonstrates Breeze and KnockoutJS working together in a single page CRUD (Create/Read/Update/Delete) application.

The user experience is the same for this and all Todo Sample variations. The source lies within the "Samples" package which you can download here.

App Architecture

Todo is the simplest full-CRUD app we could think of. The architecture is deliberately primitive and simplistic. There's only one model type (TodoItem) and only one screen. It's all about the mechanics of manipulating data and presenting them for user review and edit.

The entire app is organized in a single .NET web application project that hosts both server-side and client-side components. The three items outlined in red are the client; everything else is server-side.

Server

The server is a simple ASP.NET Web Application. It hosts the client-side assets (HTML, CSS, and JavaScript). It also hosts an ASP.NET Web API service consisting of a single Breeze-styled Web API controller in front of an Entity Framework "Code First" model talking to a SQL Server database.

The server is identical across all Todo sample variations.

Client

There are no client-side .NET dependencies: no ASP.MVC, no Razor, just pure CSS, HTML, and JavaScript.

The Content folder holds CSS files, the Scripts folder holds 3rd party and application JavaScript libraries; the application libraries are within an inner App folder. The index.html file holds all HTML.

We divide the client app into four functional areas:

 

Area Comment
Layout Index.html contains the "View" HTML with KnockoutJS data binding markup. It's also the application host page with css and script tags.
Presentation
Logic
JavaScript in the viewModel.js exposes data and method binding points to the view. All KnockoutJS code lives here. Many of the methods implement CRUD actions by delegating to methods of the service layer.
Model &
Data Access
JavaScript in the dataservice.js creates, queries, deletes, and saves entities using BreezeJS. All BreezeJS code lives here.
Logging The logger.js presents activity messages and errors as "toasts" popping from the lower right via the toastr.js 3rd party library.

Knockout highlights

We assume you're acquainted with KnockoutJS and that the markup in index.html and the JavaScript programming model are familiar to you.

View

The Todo app's  "View" is embedded in index.html. You'll recognize the way Knockout (KO) markup is expressed in "data-bind" attributes whose values declare bindings of buttons to actions, widget values and CSS classes to view model properties, and repeats Todo items within a list using an <li> tag template.

Here's an excerpt showing the TodoItem template within the <li> tag:

<li>
     <!-- readonly view -->
    <div data-bind="visible: !isEditing()">
        <input type="checkbox" data-bind="checked: IsDone" />
        <label data-bind="text: Description, click: $parent.edit, 
               css: { done: IsDone, archived: IsArchived }"></label>
        <a href="#" data-bind="click: $parent.removeItem">X</a> 
    </div>
    <!-- edit view -->
    <div data-bind="visible: isEditing">
        <form data-bind="event: { submit: $parent.completeEdit }">
            <input type="text" 
                   data-bind="value: Description, hasfocus: isEditing" />
        </form>
    </div>
</li>

ViewModel

All Knockout-related JavaScript is in the Scripts/app/viewModel.js file. The script returns a ViewModel object whose properties are the binding surface, the targets of the "data-bind" attributes in the HTML. The script is written in the "revealing module pattern" style that emphasizes readability. The returned module object (vm) is a hashmap whose properties are one-liners:

var vm = {
    newTodo: ko.observable(""),
    items: ko.observableArray(),
    includeArchived: ko.observable(false),
    addItem: addItem,
    edit: edit,
    completeEdit: completeEdit, 
    removeItem: removeItem,
    archiveCompletedItems: archiveCompletedItems,
    purge: purge,
    reset: reset
};

Either the implementation is crystal clear (e.g., newTodo: ko.observable("")) or is delegated to a function.(e.g., addItem: addItem) whose definition appears lower in the file.

The ViewModel definition is followed by an initialization function call (initVm()) and is then returned as the value of this ViewModel module (app.viewModel).

The private functions that do work come next. They're wrapped in a region comment (//*region private functions) that separates the high-level-responsibility code above from the implementation details below.  Visual Studio can collapse the region for easy exposition.

Here's the ViewModel initialization function

function initVm() {
    vm.includeArchived.subscribe(getAllTodos);
    addComputeds();
    getAllTodos();
}

It has 3 jobs:

  1. Re-fetch the Todos when the user ticks the "Show archived" checkbox which is bound to the ViewModel's includeArchived observable.
  2. Add three KnockOut computed observables which are functions
  3. Fetch the initial set of active (non-archived) Todos.

The Todo ViewModel Sample Design topic describes the purpose and basic mechanics of the remaining functions including the three computeds.

Dataservice

The dataservice.js file handles the creation of new Todo objects and all interactions with the server. It's written in Breeze and almost all Breeze-related code is in this dataservice. See the "Todo Sample Dataservice" page for details.

Breeze ships configured to use Knockout as the model library. When the application creates new Todos or materializes Todos from query result data, Breeze instantiates Todo objects with Knockout observables in lieu of JavaScript properties. For example, the server-side TodoItem.Description property becomes the KO item.Description observable function; you get the value by calling item.Description() and you set the value by calling item.Description("Learn Breeze").

Logger

The logger.js file is an abstraction for logging messages and displaying them to the user. Internally it uses the open source toastr library to display messages as "toasts" that float up from the bottom right of the screen.

This file is identical across all Todo sample variations.

Back to the main Todo Sample page