The Breeze/Angular SPA Template

The Visual Studio ASP.NET Breeze Angular SPA Template generates a simple Todo application that illustrates many of the essential techniques for building a Single Page Application ... with AngularJS and Breeze.

Breeze is an open source library for building rich data applications in HTML and JavaScript. You can learn more about Breeze by trying the online tutorial and browsing the documentation on this site

The Breeze Angular SPA Template is available as a Visual Studio Extension (VSIX). After installation it appears in the "ASP.NET MVC 4 New Project" dialog. Select it and within minutes you'll be running the Todo app in your browser. You'll find an overview of Todo on Microsoft's Breeze/Angular template webpage.

Template Overview

The Breeze Angular SPA template generates a single-project, web application with structure and contents very similar to the ASP.NET MVC 4 “Single Page Application” template. That similarity is intentional. A primary goal of the Breeze templates is to demonstrate how little must change at the data access layer to add data-rich Breeze development. We'll highlight the Breeze parts as we explore this template.

Here's the main Todo page:

Unlike the ASP.NET SPA template, the Breeze Angular template generates a two-page application. The second "About" page displays a log of "events" recorded during the current user session:

The user toggles between the two pages via the  light-gray "About" and "Todo" links in the upper left. Page generation and management happens within the browser; the server plays no part.

AngularJS

Data management with Breeze is certainly a major focus of this template. Presentation with AngularJS is the other principal theme. AngularJS  (abbreviated 'Ng') is a library with comprehensive support for JavaScript client applications.

This template concentrates on Angular data binding and how that shapes the way you write a Breeze SPA application.  Angular does much more than data binding. It also has an in-browser pub/sub event manager, screen routing, and dependency injection. We'll see those features in action. But we'll look mostly at data binding.

When you build an app with data binding you don't push data into controls and pull values out. You declare a relationship between View widgets and a non-visual code component (e.g., Controller or ViewModel) and let a library do the push/pull grunt work. 

As with other data binding systems, you add special declarative binding markup to your HTML Views.  This markup tells Angular how to coordinate controls and css classes with the members of a "scope" object. You define that "scope" object in a JavaScript Controller.

Angular markup is familiar to many when written in "handlebars" notation ("{{...}}}").  The real power of Angular is in its "directives" which are bindings expressed as custom HTML tags, attributes or classes. You can extend the Angular binding vocabulary by writing your own directives. It can feel like you're defining your own HTML language. It's not just fun. It's a clean and effective way to keep JavaScript out of the HTML and keep HTML out of your JavaScript.

The really big difference between Angular and other data binding libraries is in how Angular binding does its thing. Most libraries rely on Model and ViewModel/Controller events to tell the binding system when to update UI controls. Angular doesn't use events. Angular does'dirty checking'. Periodically, during the Angular "digest cycle", it compares every data bound DOM element with the corresponding data value. It's a radical approach ... that works!

Let's see it work ... in the sample application generated by the Breeze/Angular SPA template.

Project Overview

Here is the project structure of an application generated by the template ... an app we happened to call "AngularSpa":

SPA Template Project

app – The application JavaScript and HTML files that drive the client experience.

App_Data – The Todo LocalDb database files; “Show all files” to see them.

App_Start – Server-side web application configuration classes

Areas – ASP.NET MVC location for Web API help

Content – CSS and theme images

Controllers – both MVC page and Web API data controllers

Filters – Custom Web API attributes to repel AJAX CSRF attacks and to creates the database when not found.

Models – The server-side, CLR model classes

Scripts – 3rd party JavaScript files in support of the client app.

Views - HTML for both ASP.NET MVC (not SPA)

The AngularSpa project defines an MVC 4 web application that serves content and data. Most of the 170+ files never leave the server. What’s really interesting about SPA happens on the client. We’ll look at the client first and then circle back to the server.

Client

In a typical session roughly 30 files - style sheets, scripts, and HTML files - make it over to the browser.  We can ignore the style sheets; all but two are themes from jQuery-UI.

Ten scripts are 3rd party libraries:

  • jQuery (5 libraries and plugins)
  • breezeJS – for data management
  • breeze.savequeuing.js – a Breeze plugin to handle concurrent client save requests.
  • angular.js – for data binding, dependency injection, in-browser routing and a whole lot more
  • modernizr – to reduce the effects of browser differences
  • q.js – a promises library to simplify asynchronous programming, required by Breeze

We’ll brush past ajaxlogin.js which come to us unadulterated from the ASP.NET SPA template. It's a traditional jQuery JavaScript file in support of the MVC views devoted to authentication tasks: login, logout, and account control. It has no role in the SPA page.

Only 3 HTML files and 6 application scripts located in the app folder define the SPA proper.

Client stack

Views

This SPA has two views (todo and about) that are hosted within the Index.cshtml shell web page.

Views/Home/Index.cshtml is an MVC web page written with the Razor rendering engine. It is the proverbial "single page" of a "Single Page Application" (SPA).  Scroll to the middle until you come to this:

<div id="main-content" data-ng-app="todo">
@if (@User.Identity.IsAuthenticated)
{
    <div data-ng-view></div> <!-- The SPA Views go here -->   
}
else
{
    @Html.Partial("_Authentication")
}
</div>

The "if" block hosts the SPA views. The user sees these views only after having registered and logged in. The "else" block hosts the authentication screens. We won't cover the authentication process which is copied directly from the ASP.NET SPA template.

The Angular-controlled content

The top <div> has an Angular attributes whose name begins "data-ng".

    <div id="main-content" data-ng-app="todo" >

Angular conventions support several ways to identify Angular markup attributes. This view is coded for HTML5 so we've chosen the "data-" prefix style which triggers fewer complaints from tooling that tries to enforce HTML5 coding rules.

The "data-ng-app" attribute identifies the Angular application module, "todo", that governs the <div>. The "todo" module, defined in todo.main.js, bootstraps the application. The angular.js script discovers this attribute on the <div> and starts analyzing its contents for instructions (called "directives") telling it which objects to create and how to bind HTML widgets to those objects.

.It finds the next <div>

    <div data-ng-view></div> <!-- The SPA Views go here -->

The "data-ng-view" attribute identifies the place within the shell where the "todo" module should insert the application views.  When we get to the module, we'll learn about screen routing and how Angular generates the two views based on the "view template" files, todo.view.html and about.view.html.

A taste of Angular data binding

A full blown Angular tutorial is out-of-scope for this topic. The angular web site is full of great, in-depth material for you to study.

We will point out a few things in the app/todo.view.html  template to give you a sense of how Angular data binding works. We begin by recalling that the app presents one or more TodoLists in the guise of sticky notes.

Two TodoLists

To see the corresponding layout and markup, scroll down a few lines until you get here:

<p class="error" data-ng-show="error">{{error}}</p>
<button data-ng-click="addTodoList()">Add Todo list</button><br/>
<article class="todoList" data-ng-repeat="list in todoLists"> 
    ...  
</article>

The <p> element displays a message for fatal application error ... which you should never encounter if all goes well.

It demonstrates two forms of Angular binding. The first is attribute binding of the kind we saw earlier. The "data-ng-show" directive means "only show this paragraph if the controller sets the error property."  The paragraph will be invisible if the error property is "falsey" (e.g., a null or empty string). Otherwise it springs into view.

Angular would insert the value of the error property into the space marked by {{error}}. The double braces are called "handlebars".

The <button> demonstrates attribute binding to a controller method. When the user clicks the button, the Angular "click" directive calls the addTodoList method on the controller.

We hope you’re starting to get the picture.

Angular repeaters and templates

The <article> element is bound to the Todo lists with an Angular repeater directive. In plain English, it says

Take each list item from the Controller’s todoLists array property and bind it to the template defined within this <article> element.

Angular repeats the <article> template layout for each TodoList object in the controller's todoLists array. The scope of each templated instance will be one of those TodoList objects and that scope's name will be "list". Here's the TodoList title followed by the HTML snippet from that describes the layout

<form data-ng-submit="endEdit(list)">
    <input data-ng-model="list.title" 
           data-selected-when="list.isEditingListTitle"
           data-ng-click="clearErrorMessage(list)" 
           data-on-blur="endEdit(list)"/>           
</form>

Here we see that we can bind both to the controller (the anonymous outer scope) and to the TodoList object (the inner scope named "list").

The <form> element wraps a single textbox. The "ngSubmit" directive will call the controller's endEdit method with the TodoList when the user presses the [Enter] key. It also suppresses what would otherwise result in a postback to the server.

The textbox itself is bound (via the "ngModel" directive) to the inner scope by virtue of the attribute value, "list.title". The "title" is a property of a TodoList, not of the controller.

Again, we hope you're starting to grasp the way binding works within repeaters ... and sensing the power of Angular data binding.

Custom directives

This snippet has five different "directives". How many directives does Angular have? A substantial number, actually.

But Angular doesn't have a directive for every situation. In fact, two of the directives here were written expressly for this sample application. The "selectedWhen" directive selects the title textbox when the TodoList.isEditingListTitle is true. That property becomes true when you create a new TodoList. Here's what it looks like:

When the user either presses the [Enter] key or leaves the textbox, the user is done editing the title and the controller should do something (whatever endEdit does). The <form> "ngSubmit" handles the [Enter] key. When the user leaves the textbox, the browser raises its "blur" event; the "onBlur" directive hears that event and calls endEdit.

Angular doesn't have "selectedWhen" and "onBlur" directives. We had to write them ourselves ... and did so in todo.main.js. The ability to extend the binding system with custom directives is one of Angular's greatest attractions.

Nested repeater

Rummage around within the <article> template and you’ll find another repeater.

<li data-ng-repeat="item in list.todos"> ... </li>

Every TodoList has its own collection of TodoItems. The template within the <li> describes the layout for one of those TodoItems. Each TodoItem becomes a third nested scope (controller ->  TodoList -> TodoItem).

Here are three TodoItems bound to this template followed by the template itself:

Three TodoItems

<input type="checkbox" 
       data-ng-model="item.isDone" 
       data-ng-change="endEdit(item)"/>
<input type="text" class="todoItemInput" 
       data-ng-model="item.title" 
       data-ng-disabled="item.isDone"
       data-on-blur="endEdit(item)"
       data-on-enter="endEdit(item)"
       data-ng-click="clearErrorMessage(item)"  />
<a href="#" data-ng-click="list.deleteTodo(item)">X</a>
<p class="error" data-ng-show="item.errorMessage">{{item.errorMessage}}</p>

Some observations:

  • The "ngModel" directive tells Angular which property provides the data for the control (checkbox or textbox)
  • Several user gestures can trigger a call to the controller's endEdit method. The "ngChange" directive listens for one such gesture, the tick of the checkbox.
  • The "ngDisabled" directive adds the “disable” attribute to the <input> element when the TodoItem is done, thus preventing changes to its title
  • The custom "ngBlur" directive appears again, this time triggering the controller's endEdit method when the title textbox loses focus.
  • "onEnter" is another custom directive that listens for the [Enter] key and calls the controller's endEdit method.
  • clicking in the textbox calls a controller method to clear the error message for this item.
  • The little “x” after the title is a link bound to the parent TodoList's deleteToDo method. We’ll be looking for that method when we examine the TodoList initializer in todo.model.js.
  • A TodoItem has an errorMessage property which, if other than an empty string, will appear in a paragraph below the title as we see in the third displayed TodoItem.

Todo Module

We've got a sense of the Todo view. Let's look at the code to support it.

An Angular app requires at least one module, a module to bootstrap the application. This is the module we mentioned earlier when we located the "ngApp" directive in the outer <div>.  The module for this sample app is called "todo" and it's defined in the app/todo.main.js file. It doesn't take much to define it

window.todo = angular.module('todo', []);

Now the "todo" modules is in the global namespace. It's the only application component that will be in the global namespace; all other components belong to this "todo" module.

The rest of the file consists of statements that extend the module with functionality. In fact, that's what the other application JavaScript files do too; they add features to the module.

Dependency Injection

The first new feature we encounter is the addition of the Breeze and the Q.js promises service objects to the module's dependency injection container.

  todo.value('breeze', window.breeze)
      .value('Q', window.Q);

Dependency injection is a key, inescapable feature of Angular. It appears everywhere in Angular's own code and you, the developer, are expected to write your code using the Angular dependency injection system.

The author of this SPA template wants Angular to inject all dependencies into the application components. That includes the dependencies on 3rd party libraries such as Breeze and Q. So he fished Breeze and Q out of the global namespace and passed them as "values" to the module. We'll see in a moment how Angular injects this Breeze service into our application controller.

Your app doesn't have to use dependency injection to access Breeze or Q . For example, in a Duradal application, you simply reference Breeze from the global namespace. Whether you inject or reference global 3rd party service objects is a matter of personal or team style.

Screen routing

The app presents two views, a todo view and and about view. The user can toggle between these views by clicking the page links in the upper left corner.

Each click changes the screen almost completely as if navigating from one web page to another. In fact, we've navigated within the same web page, within Index.cshtml, a fact that can be confirmed by looking at the browser address bar.  Notice that both pages have the same URL path, followed by a "#", and then a fragment. Only this hash fragment changes when the user clicks a page link.

Angular is making and managing these addresses and ensuring that the underlying HTML document doesn't change from the browser's perspective.

The addresses themselves are defined in routing instructions added to the "todo" module:

// Configure routes
todo.config(['$routeProvider', function ($routeProvider) {
      $routeProvider.
          when('/', { templateUrl: 'app/todo.view.html', controller: 'TodoCtrl' }).
          when('/about', { templateUrl: 'app/about.view.html', controller: 'AboutCtrl' }).
          otherwise({ redirectTo: '/' });
  }]);

Angular documentation is the place to learn about routes. In brief, this code defines a routing function. The routing function takes an injected Angular $routeProvider service. The function adds three routes to the service:

  1. a route to the "todo" view
  2. a route to the "about" view
  3. the fallback route, for when the URL doesn't match one of the other routes. The fallback is the "todo" view.

A little interpretation might help. The second route reads "when the URL ends in '/about', get the 'about.view.html' as the view template and bind it to an instance of the Angular Controller named 'AboutCtrl' "

Here's the HTML near the top of the todo page for the page link that invokes this route:

<a href="#/about" class="pageLink" >About</a>

Before we leave todo.main.js to discuss Controllers, observe the custom directives that we mentioned earlier in our discussion of the view. They are all defined here as features of the todo module.

Controller

This application has two Angular controllers, one for the todo page and one for the about page. We'll focus on "TodoCtrl" which is defined in the todo.controller.js file.

todo.controller('TodoCtrl',
    ['$scope', 'breeze', 'datacontext', 'logger',
    function ($scope, breeze, datacontext, logger) {...}]);

Notice that we're adding it to the "todo" module via that module's controller method. That method has two parameters:

  1. the name of the controller
  2. an array of dependency names followed by a definition function

The second parameter ... is a little ... weird. An array ... mostly of strings ... with a function as its last element. Only in JavaScript would you encounter such syntax. You'll just have to get used to it.

The string names refer to injectable services. Breeze is one of those services; we added it to the Angular injection container during the definition of the todo module. The "datacontext" and the "logger" are application services, described below.

"$scope" is the Angular name of the binding object through which this controller controls the view; more on that in a moment.

These three strings parallel the parameter names of the function which defines this controller. That parallelism is by design. Angular needs to know the names of the services it will inject at runtime ... one service for each parameter of the function.

You ask: "Why does Angular need the string names when it could read the function parameter names? They're the same names."  Indeed they are ... for the moment. But the day will come when you minify this application. The minifier will change those names from $scope, breeze, and datacontext to something shorter and incomprehensible like a, b, and c. There are no services named 'a', 'b', or 'c'. The string names won't be minified and will tell Angular what you really intended.

Pretty much everything you write is written in this style, relying on Angular to inject the services your object needs.

$scope

The most important parameter to a controller's definition function is $scope.  The view is bound through Angular directives to the properties and methods of this $scope object.

Angular is nominally an MVC (Model-View-Controller) architecture; in practice it feels like an MVVM (Model-View-ViewModel) with the Controller + a "scope" object playing the role of the ViewModel.

When Angular calls the controller definition function, it pass in a scope object which is essentially empty. Your controller definition function adds properties and methods to the scope object and through those members it controls the view. For example, the controller adds a todoLists array to the scope. Later, it retrieves data (via the datacontext), fills the array, and the retrieved Todo data appear on screen. The controller adds its addTodoList method to the scope. Later, when the user clicks the "Add Todo list" button, an Angular binding invokes this method.

This "TodoCtrl" adds eight members to its $scope.

$scope.todoLists = [];
$scope.error = "";
$scope.getTodos = getTodos;
$scope.refresh = refresh;
$scope.endEdit = endEdit;
$scope.addTodoList = addTodoList;
$scope.deleteTodoList = deleteTodoList;
$scope.clearErrorMessage = clearErrorMessage;

Get Todos

When Angular creates this controller and binds it to the view, the last thing the controller does is get the Todo data to display to the user.

// load TodoLists immediately
$scope.getTodos(); 

Of course the controller won't get the data itself. Data access is not its responsibility ... nor should it be. Instead it delegates to a method of the datacontext and processes the result/errors in its callbacks.

function getTodos(forceRefresh) {
    datacontext.getTodoLists(forceRefresh)
        .then(getSucceeded).fail(failed).fin(refreshView);
}
function refresh() { getTodos(true); }

function getSucceeded(data) {
    $scope.todoLists = data;
}
function failed(error) {
    $scope.error = error.message;
}
function refreshView() {
    $scope.$apply();
}

All remote data service operations are asynchronous. Angular and Breeze use Q.js promises to wait for asynchronous operations to complete. In this case, If the "get" operation succeeds, the controller resets the scope's todoLists array with the queried Todos into. It sets the scope's error message variable if the "get" fails.

Angular is unaware of asynchronous operations. It can't update the screen on its own. The controller must tell Angular to refresh the screen when the operation finishes, whether the operation succeeded or failed. That's why the fin() method calls $scope.$apply, to trigger the Angular "digest cycle" and update the screen.

Refresh

Notice that getTodos() takes an optional "forceRefresh" parameter which is set true within refresh. What's that about?

It's about caching and the ability in Breeze to query the cache as well as the server. By default, this controller will fetch TodoLists from the Breeze local cache, not the server.

In this app, the user can toggle quickly between the TodoList page and the About page. Every return to the TodoList page causes Angular to create a new controller and view instance. A new controller always loads TodoList data as we just saw. Do we really want a server request each time?

Probably not, especially if there is a delay in server response as there certainly will be once this application is in the field.The data might have changed on the server in the interim; that's unlikely when I'm the one toggling rapidly between the pages of my personal Todos.

The About page tells the story.

  • Open the developer tools (F12),
  • choose the network tab,
  • filter for AJAX traffic only (in Chrome click the 'XHR' button at the bottom),
  • reload the browser page,
  • flip several times between the two pages.
  • while on the TodoList page, click the "Refresh" link at top left.

You should data traffic at the beginning and end but not in the middle. The event log on the About page should look something like this (emphasis mine):

1:[info] creating datacontext
2:[info] creating TodoCtrl
3:[info] remote query succeeded
4:[info] creating TodoCtrl
5:[info] local query succeeded
6:[info] creating TodoCtrl
7:[info] local query succeeded
8:[info] creating TodoCtrl
9:[info] local query succeeded
10:[info] remote query succeeded

The initial view must get the data from the server. Subsequent views (controllers) are satisfied with a query from the cache. The user can force a refresh at will or we could come up with some other programmatic means (timer, push notification) to trigger a refresh. We'll see how to code a cache query in the datacontext.

DataContext

The todo.datacontext.js file defines a datacontext service object that handles all remote data access.

There is very little Angular specific code in the datacontext. It's almost pure JavaScript and Breeze. Angular references appear in the factory signature and exactly one other place; see if you can find it ;-).

Here is the "public" API of the datacontext service.

var datacontext = {
    metadataStore: manager.metadataStore,
    getTodoLists: getTodoLists,
    createTodoList: createTodoList,
    createTodoItem: createTodoItem,
    deleteTodoItem: deleteTodoItem,
    deleteTodoList: deleteTodoList,
    saveEntity: saveEntity
};

The method names give a clear indication of their purpose. Their implementations deserve some attention.

Configuring for Breeze

We meet Breeze for the first time in todo.datacontext.js. It begins with configuration:

configureBreeze();
var manager = new breeze.EntityManager("api/Todo");
manager.enableSaveQueuing(true);

The first step configures Breeze itself via the configureBreeze method, defined near the bottom of the file. Scroll there now.

Breeze is configured by default to communicate with the Web API and to use Knockout for data binding. We are using the Web API in this sample but we're using Angular for data binding. So we tell Breeze about that:

// configure to use the model library for Angular
breeze.config.initializeAdapterInstance("modelLibrary", "backingStore", true);

There is no special "modelLibrary" adapter needed for Angular. Breeze's own "backingStore" adapter works fine.

By default, Breeze would use the same spelling for property names on both client and server. We want to follow the JavaScript standard of camel case property names (e.g., “todoListId”). The corresponding CLR class names on the server are in Pascal case (e.g., “TodoListId”). We tell Breeze to use the camel case naming convention instead of its default.

breeze.NamingConvention.camelCase.setAsDefault();

Breeze will translate the names accordingly as it makes requests and receives responses from the server.

The last chunk of configuration code concerns cooperation with the anti-CSRF attack measures implemented on the server. The details of the attack and effort to repel it are out of scope. But the client's part in this - the setting of a special HTTP header for every AJAX request - demonstrates one of the ways that you can intervene in Breeze AJAX processing. Breeze uses an "AJAX adapter" to make AJAX requests. By default that adapter uses jQuery.ajax. You can substitute any alternative with the same API.

We don't have to replace the adapter in this sample because the default version lets us specify default headers for all requests. Here's how we get the AJAX adapter and set the header required for the Web API anti-CSRF mechanism:

var ajaxAdapter = breeze.config.getAdapterInstance("ajax");
ajaxAdapter.defaultSettings = {
    headers: {
        'RequestVerificationToken': antiForgeryToken
    },
};

That's it for configuration. Please scroll back to the top of the file where we create the application's Breeze EntityManager.

Create an EntityManager

We’ll need a Breeze EntityManager to handle communications with the server and to hold the cache of entities that we query, create, and save. We create one, specifying the endpoint of our Web API controller (discussed below) as the constructor parameter.

var manager = new breeze.EntityManager("api/Todo");

The datacontext holds on to this manager for the duration of the user session. Every entity we create, modify, query and save resides in this manager’s entity cache.

Let’s skip the next line of manager configuration. We’ll get to it later when we talk about saving changes.

Querying with Breeze

 Let’s see how Breeze gets Todo data with its getTodoLists method:

function getTodoLists(forceRefresh) {

    var query = breeze.EntityQuery
        .from('TodoLists')           // query the "TodoLists" resource
        .expand('Todos')             // include TodoItems in the response payload
        .orderBy(';todoListId desc');// sort the TodoLists by Id on the data tier

        // if already initialized and not forcing a refresh
        // set the query to target the cache, not the server
         if (initialized && !forceRefresh) {
            query = query.using(breeze.FetchStrategy.FromLocalCache);
         }
        initialized = true;         // becomes true after the first query

        return manager.executeQuery(query)
               .execute()           // execute the query, returning a promis
               .then(getSucceeded); // call 'getSucceeded' if all is well
                                    // caller to handle failure
}

The method begins by defining a query object. It identifies the resource, expands to include the related TodoItems, and sorts the result by the TodoList id, latest one first.

The resemblance to .NET's LINQ syntax is intentional. Breeze uses method chaining to build up a query object. We’re barely tapping the range of possibilities in this sample.For example, we're not filtering at all. We're retrieving all TodoLists. That suits a simple sample but isn't realistic. Checkout the query examples in the breeze documentation.

The getTodoLists method can query the server or the cache. Breeze queries target the server by default. In this app, we want to query the cache by default. Unless this is the first query or the caller demanded a remote query (forceRefresh === true) , we'll change the query's default FetchStrategy to target the manager's cache.

Then we run the query. The executeQuery method is asynchronous. Like all Breeze asynchronous methods it returns a promise – a promise to tell the client what the server said when it responded to the request. The flow of control continues until the server responds. If the query succeeds, the promise calls getSucceeded, passing in the Breeze query result object.

function getSucceeded(data) {
    var qType = data.XHR ? "remote" : "local";
     logger.log(qType + " query succeeded");
    return data.results;
}

The query result members describe various aspects of the response. The most important member is the array of results which we'll return to the caller (which receives them via its own success callback).

The function logs what happened before returning, reporting whether the Breeze queried the server or the cache.

We could have used captured variables to make that distinction. But its more instructive to deduce the difference from the query result object.

If Breeze queries the server, the query result object has an XHR object associated with the AJAX call; that's in the result to help the developer analyze the response in more complex scenarios. If Breeze doesn't query the server, there is no AJAX call and there won't be an XHR object.

Query results

The queried Todo data arrive from the server as JSON. After they arrive Breeze converts the JSON data to Breeze TodoList entities. Each TodoList is held in the manager’s cache where it will be watched for changes.

Remember that the query had an expand clause for TodoItems. Each TodoList has a navigation property (todos) that returns an observable collection of its child TodoItems. These TodoItems were extracted from the query response payload, loaded into the manager’s cache, and attached to their parent TodoLists ... automatically by Breeze.

Creating new Todo data

This application creates new Todo model objects in 3 steps:

  1. Create a new entity
  2. Set its initial values
  3. Save it

Creation of a new TodoList in the todo.controller is one place to see this pattern:

function addTodoList() {
    var todoList = datacontext.createTodoList();
    todoList.isEditingListTitle = true;
    datacontext.saveEntity(todoList)
        .then(addSucceeded)
        .fail(addFailed)
        .fin(refreshView);
}

Notice the two calls to the datacontext methods: once to create the TodoList, the second to save it. Here is the first of those methods:

function createTodoList() {
    return manager.createEntity("TodoList");
}

You generally don’t “new up” an entity in Breeze. Instead you call the EntityManager.createEntity factory as we did here.

We’ll look inside the datacontext’s saveEntity method in a moment. From the outside we see that it returns a promise. A save is an asynchronous operation. Eventually it either succeeds or fails and we invoke addSucceeded or addFailed accordingly.

Save Changes

In most non-Breeze applications, you're likely to see separate save operations for each kind of entity change: add, modify, and delete. And each operation only saves one instance of one type of entity at a time. That's how it works in the datacontext of the ASP.NET SPA template.

That’s ok for this simple Todo example which saves after every change of any kind. That’s not how most real applications work. They aren’t that chatty.

In a “real Todo app”, you might create a new TodoList, add several TodoItems to it, and then save all of your changes at once in a single batch. Or you might delete one TodoItem, mark another one done, and add a new Todo before clicking a save button and saving these diverse changes as a batch.

The ASP.NET SPA would have been much more complicated if it had to meet these requirements. Breeze, on the other hand, is designed for these scenarios.

Breeze accumulates changes in a manager’s cache. When you’re ready, you call EntityManager.saveChanges() and they all go together to the server as a single save request. In the Breeze SPA template app we had to bend the one-entity-at-a-time pattern to the Breeze way by routing each single-object-save through a common saveEntity method. The core of the datacontext save method is as follows:

function saveEntity(masterEntity) {
    ...
    return manager.saveChanges().then(saveSucceeded).fail(saveFailed);
    ...
}

Bail out if nothing to save

Breeze checks its cache for unsaved changes. If there are none, it returns immediately along the success path without making a call to the server. You can often just let this happen.

But in this sample, because we're logging save events in this sample, we intercept a "null save" explicitly and exit immediately without logging:

// if nothing to save, return a resolved promise
if (!manager.hasChanges()) { return Q(); }

The caller expects a promise so we ask the Q.js promises library to make one that has resolved successfully.

Describe the save

We don’t need the "master entity" to perform the save. We’re saving all entities in cache with pending changes. Most of the time in this app, there is only one unsaved entity in cache. We only need the "master entity" to describe the save operation in log and error messages:

var description = describeSaveOperation(masterEntity);

Save failure

The save success callback is trivial; it simply logs that the save succeeded and reports which entity it saved (or the "master entity" if there were many entities to save).

function saveSucceeded() {
    logger.log("saved " + description);
}

But if the save fails, we want to analyze the failure and take steps to set things right. The hard part isn't detecting failure. It's presenting the user with helpful information about that failure ... which is why the bulk of this save code seems unexpectedly complex.

function saveFailed(error) {
    var msg = getErrorMessage(error);
    logger.log("save failed ... " + msg, 'error');
    // Let user see invalid value briefly before reverting"
    $timeout(function() { manager.rejectChanges(); }, 1000);
    throw error; // so caller can see failure
}

When something bad happens, the safest recourse is to tell the user about the problem, reverse all of the pending changes by calling manager.rejectChanges, and invite the user to try again. We’ll give the user about one second to see both the error message and the errant entity values before rolling back the changes. We use Angular's $timeout service in order to refresh the screen after rolling back the changes.

A save could have failed for a variety of reasons. A validation error is a common cause.

Breeze Validation

The CLR classes on the server impose certain restrictions on some of the entity properties. For example, the TodoList and TodoItem Title properties are required and have maximum lengths.

Breeze captures and forwards these constraints as part of the metadata it sends to the client. BreezeJS turns these constraints into client-side validation rules and executes these rules at particular moments in the entity life-cycle. One of those moments is when you save changes.

Breeze runs the validation rules for every entity before saving it. If any entity fails a validation rule, Breeze terminates the entire save without contacting the server. The user doesn’t have to wait for the server to tell him he made a mistake.

Every entity maintains its own collection of validation errors. That collection is empty when the entity passes validation. It won’t be empty if the entity fails validation. The datacontext.getValidationErrorMessage method shows how to extract the validation error message from the validation error collection:

function getValidationErrorMessage(error) {
    try { // return the first error message
        var firstItem = error.entitiesWithErrors[0];
        var firstError = firstItem.entityAspect.getValidationErrors()[0];
        return firstError.errorMessage;
    } catch (e) { // ignore problem extracting error message 
        return "validation error";
    }
}

To see validation in action, enter an especially long name for a TodoItem. The screen should display something like this:

Too long message

Configuring the manager for saves

We conclude the datacontext review by returning, as promised, to the two lines of manager configuration near the top of the file.

Save Queuing

A save is an asynchronous operation involving a communication with the server. We can’t tell if the save succeeded or failed until the server returns with a response. That could take a while.

If we try a second save while a previous save is in progress, Breeze might try to add or delete the same entity twice. Therefore, the Breeze manager refuses to save a second time while another save is “in flight”.

There is a problem. This application saves every time the user makes a change. There is no save button and no way to disable user changes while we’re waiting for a server response. The user could easily make another change, triggering another save before the first save completes. The manager will refuse and throw an error. We don’t want that.

Fortunately, there is a Breeze plugin, breeze.savequeuing.js, that can queue the second save while the first save is “in flight”. We enable it for this manager with this line near the beginning of the file:

manager.enableSaveQueuing(true); 

Save on modify

This application saves immediately when the user adds, modifies or deletes a TodoList or a TodoItem. It takes a dedicated method to add or delete an object. Such methods are defined in this data context and it’s pretty easy to wire in a call to the manager’s save method as we’ll see.

But modifying an existing entity is a little different. We might change the title. We might check/uncheck the checkbox bound to its isDone property.  Somehow we have to detect any change to the object and save it.

We could setup $watches for every ngModel. This effort doesn't seem onerous when there are only three properties to worry about (TodoList.title, TodoItem.title, TodoItem.isDone). It would be madness to add thousands of  $watches to entities with the hundreds of properties typical of a real model. Maybe there's a simple approach in Angular unknown to us.

We haven't looked too hard because there is an easy alternative in Breeze. Breeze “entities” are self-tracking. Each entity maintains an EntityState which is accessible through its entityAspect property. A change to any property of an unmodified entity flips its EntityState to “Modified”.

We can listen in exactly one place for the EntityState to flip on any entity. That place is the manager that holds the entity in its cache. Here’s how:

manager.entityChanged.subscribe(entityStateChanged);

The entityStateChanged handler suffices for every entity. All it must do is examine the entity to see if it flipped to “Modified” and, if so, save it.

The last step of manager configuration sets up this listener:

configureManagerToSaveModifiedItemImmediately();

Model

The todo.model.js file defines the client-side Todo object model.

Open the file and discover something surprising. The persistent properties of TodoItem and TodoList are not defined!

In the ASP.NET SPA model.js, you had to define every data property like this:

// The ASP.NET SPA template uses knockout; this is KO syntax
self.todoItemId = data.todoItemId;
self.title = ko.observable(data.title);
self.isDone = ko.observable(data.isDone);
self.todoListId = data.todoListId;

There is no such code in the Breeze SPA templates because Breeze generates entity type definitions from metadata.

Where do metadata come from? Most Breeze applications get their metadata from the server. A Breeze client can get metadata from an OData data source. It can also get metadata from a suitable Web API controller such as a Breeze controller fronting an Entity Framework model. That’s how this Breeze SPA gets its metadata.

Extending entity definitions on the client

Metadata from the server can only describe properties of the classes exposed on the server. We often need to enrich a client-side model type with properties and behavior that exist only on the client.

Consider the errorMessage property. This SPA app expects every TodoItem and TodoList to have an errorMessage property. Usually that property returns an empty string. But if something goes wrong when saving the entity, the errorMessage will contain an explanation of the failure.

We never save the errorMessage to the database. The server-side TodoItem and TodoList don’t have this property. This property exists only on the client to support the user experience.

Breeze lets us extend the definition of the client entity types with custom members. We can do so in two ways: with a custom constructor or with an initializer. The Breeze documentation explains constructors and initializers in detail but you can get the gist of it by looking at the todo.model.js file which illustrates both techniques as seen in the initialize method:

var store = datacontext.metadataStore;
store.registerEntityTypeCtor("TodoItem", null, todoItemInitializer);
store.registerEntityTypeCtor("TodoList", TodoList, todoListInitializer);

We extract the metadataStore from the datacontext and then register the type extensions.The first parameter identifies the entity type. The second is the custom constructor. The third is the initializer.

You’ll see the errorMessage property added by the initializers, e.g.,

function todoItemInitializer(todoItem) {
    todoItem.errorMessage = "";
}

You’ll see a TodoList constructor that defines default values for new TodoLists:

function TodoList() {
    this.title = "My todos";        // defaults
    this.userId = "to be replaced";
}

The constructore is extended with prototype methods for adding and deleting child TodoItems.Here's the very simple deleteTodo method on the TodoList prototype:

TodoList.prototype.deleteTodo = function () {
    return datacontext.deleteTodoItem(this); // "this" is the todoItem
};

Again, following our usual practice, we delegate persistence-oriented activities to the datacontext.

This wraps up our tour of the client code. Let’s move to the server.

Server

The web application project structure should be familiar to anyone acquainted with MVC 4 projects. In this section we’ll highlight a few of the server-side files that are specific to the Breeze version of the SPA template application.

What is Breeze.NET?

Throught this section you'll be hearing quite a bit about "Breeze.NET". You thought Breeze was a JavaScript library.

BreezeJS really is a pure JavaScript technology. You do not need any Breeze on the server. The server need not use use any Microsoft technology: not IIS, not Web API, not Entity Framework, not SQL server.

But this SPA sample is generated from an ASP.NET MVC template. That means .NET on the backend. We’ve made it easier to program a .NET backend with Breeze.NET components in support of the BreezeJS pure JavaScript technology on the client. We think they'll save you time on the server that is better applied elsewhere.

App_Start/BreezeWebApiConfig

The App_Start folder holds web application configuration detail classes. The Breeze SPA template added the BreezeWebApiConfig file to define a Breeze Web API route and position it in front of the default Web API routes. Here’s the route:

  GlobalConfiguration.Configuration.Routes.MapHttpRoute(
      name: "BreezeApi",
      routeTemplate: "api/{controller}/{action}"
  );

The typical Breeze application sends data requests to a single Web API controller. The URL identifies the controller and an action to perform. Here’s a request for all TodoLists:

http://localhost:60124/api/Todo/TodoLists

We’ll look at this again soon when we examine the controller. For now we observe that the “Todo” path segment maps to the {controller} token and “TodoLists” to the {action} token.

This routing scheme differs from the default Web API route defined in WebApiConfig.cs. Rather than modify that file we added BreezeWebApiConfig.cs and gave its route precedence via the WebActivator assembly attribute at the top of the file:

[assembly: WebActivator.PreApplicationStartMethod(...)

App_Start/BundleConfig

This application uses Web Optimization to bundle and minify files delivered to the browser. We’re using Breeze so we’ve added a bundle of Breeze-related scripts:

bundles.Add(new ScriptBundle("~/bundles/breeze").Include(
            "~/Scripts/q.js",
            "~/Scripts/breeze.debug.js",
            "~/Scripts/breeze.min.js",
            "~/Scripts/breeze.savequeuing.js"));
  • q.js – a promises library to simplify asynchronous programming
  • breeze.debug.js – the un-minified breeze library
  • breeze.min.js – the minified breeze library. Web Optimization will only bundle one or the other of the Breeze libraries depending upon whether you build the application for debug or release.
  • breeze.savequeuing.js – a Breeze plugin to handle concurrent client save requests.

With those application start files squared away, we’re ready to look at the important elements of the server-side stack. The following diagram will be our guide as we work back from the client-facing Web API to the entity model.

Server stack

Controllers

By convention, the Controllers folder holds both MVC and Web API controllers. Let's compare the Breeze and ASP.NET controller folders:

Controllers

The two MVC controllers, HomeController and AccountController, are the same in both templates. The HomeController serves the SPA host page; the AccountController handles authentication-related requests (registration, login, logout, etc.).

The ASP.NET SPA app follows the “controller-per-model-type” resource-oriented approach. This has a two-entity model so the ASP.NET SPA has two controllers. A Breeze app follows the action-oriented approach and typically only needs Web API controller to cover requests for the entire model.

It only needs one Web API controller because the number of requests it must handle is comparatively few.

TodoController

The TodoController exposes only three methods:

Metadata

Get metadata about the server model

SaveChanges

Save a bundle of entity changes

TodoLists

Get “Todolist” data, potentially filtered, ordered, paged, and extended with child Todos

Metadata and SaveChanges

The first two are standard fare. A Breeze client needs metadata describing the model. Most Breeze developers prefer to get metadata from the server and this controller can supply the metadata.

The ASP.NET SPA uses jQuery AJAX calls to save each change one item at a time. Look at one of its controllers and you’ll see PUT, POST, and DELETE methods to update, add, and delete single items.

A Breeze app saves multiple changes as a bundle in a single transaction. We could create a new TodoList, add new TodoItems, modify another TodoList, and delete a third (along with its child Todos). We could POST all of these changes one change-set to this controller’s SaveChanges method. This particular sample isn’t written to do that … it would have required a much more complicated ASP.NET SPA sample. The Breeze version might have been less complicated if we had.

Query methods

The ASP.NET sample has a single GET method, TodoListController.GetTodoLists. The Breeze version has a single GET method, TodoController.Todos. Both return TodoLists with their associated child TodoItems.

Here’s the ASP.NET SPA original:

public IEnumerable<TodoListDto> GetTodoLists() {
    return db.TodoLists.Include("Todos")
        .Where(u => u.UserId == User.Identity.Name)
        .OrderByDescending(u => u.TodoListId)
        .AsEnumerable()
        .Select(todoList => new TodoListDto(todoList));
}

It filters for the current user’s TodoLists. Then it sorts by the key in descending order. Then it casts to Enumerable so it can pass data to a DTO class which fetches the related child TodoItems.

The Breeze TodoController.Todos is much simpler:

[HttpGet]
public IQueryable<TodoList> TodoLists() {
    return _repository.TodoLists;
}

Sure it delegates to a repository – that’s where we moved the filter for the current user’s TodoLists. We’ll get to the repository shortly. The key point is that it returns an IQueryable, leaving the matter of sort order and inclusion of child TodoItems to the client’s discretion.

The client can filter, page, order, expand, and select a subset of TodoList properties by sending OData queries to this action:

http://localhost:60124/api/todo/todolists?$filter=TodoListId eq 1 
http://localhost:60124/api/todo/todolists?$orderby=Title
http://localhost:60124/api/Todo/TodoLists?$orderby=Title&$skip=1&$top=1
http://localhost:60124/api/Todo/TodoLists?$expand=Todos

The client may be able to satisfy all of its data retrieval requirements by this single query method alone, thus maintaining a lean controller. When a single IQueryable method won’t suffice, you can add specialized GET actions to the controller and call them from the Breeze client.

It follows that the controller grows slowly, as slowly as one query method per exposed model type. For example, if the client needed TodoItems, independent of their parent TodoLists, we could add a fourth “TodoItems” query action method similar to the TodoLists method.

Aside from granting the client more flexibility for free, this approach also performs much better. The client-specified filtering, ordering, paging, and expand will execute on the data tier, not on the server tier.

The original ASP.NET SPA query casts to IEnumerable  which means the server will fetch every one of the user’s TodoLists even if the client queried for only one of them. Worse, the DTO makes a second trip to the database to get the child TodoItems for each and every TodoList.

Of course you’ll never notice in this application which doesn’t do any serious querying.  Upon launch it asks for all TodoLists belonging to the current user and that’s the last time it asks for any data. We’ll have to imagine how the controllers will be affected by richer client requirements.

TodoController attributes

The Breeze controller is decorated with two attributes

Authorize

The Web API attribute that prevents unauthenticated users from reaching the controller.

BreezeController

Configures this controller to talk to Breeze clients. It replaces all filters with a Newtonsoft Json.NET filter configured for Breeze that serializes and deserialized data as JSON. It also installs the Breeze Action filter that interprets OData query parameters and applies them to the IQueryable objects returned by such GET action methods as TodoLists; it replaces a similar Web API filter for this controller only.

Models

Let's again compare the folders produced by the Breeze and ASP.NET SPA templates:

Models

The SPA sample apps rely on Entity Framework (EF) Code First models. The model entity classes and the TodoItemContext (a DbContext) are almost identical in the two templates. The only entity class changes: we added MaxLength attributes to the Title properties so we could demonstrate Breeze validation. Breeze includes the maximum string length and required constraints in the metadata it sends to the client. We get these validations for free in Breeze. The user learns about validation errors immediately, without a delayed rejection from the server. Of course EF will also apply these validations on the server automatically; that’s a feature of EF’s DbContext.

The Breeze model omits the DTO classes which are neither needed nor wanted. The shapes of the TodoItem and TodoList types are the same on client and server so there is no reason to complicate the model with DTO classes.

The Breeze model adds a TodoRepository to encapsulate business logic governing client access to model data.

Models/TodoRepository

The TodoRepository mediates between the Web API controller and the Entity Framework which provides access to the database (as it does for the ASP.NET SPA app).

We could have written the business logic inside the controller.  Instead, the three client-facing methods of the TodoController delegate to an instance of this TodoRepository class.  We’re conforming to a general principle of Web API controller design which argues for simple implementations. The controller is supposed to direct traffic, not do heavy work of its own.

The TodoRepository inherits from a Breeze.NET helper class, the EFContextProvider, which vastly simplifies interactions with the Entity Framework.  You can read about it here.

The repository is a good place to start putting server-side business logic. A typical demo wouldn’t have business logic which is why many samples simply delegate directly to the EFContextProvider.SaveChanges method. This SPA sample actually has some business logic. Let’s take a peek.

Query constraints

Here is the TodoLists query to which the TodoController delegates its own TodoLists GET method:

public DbQuery<TodoList> TodoLists {
    get {
        return (DbQuery<TodoList>)Context.TodoLists
            .Where(t => t.UserId == UserId);
    }
}

Context is a typical Entity Framework DbContext object sporting named DbQuery properties. In this app a user may only see her own TodoLists; we’re enforcing that rule in the repository rather than in the controller.

Save constraints

The server shouldn’t trust the client. That’s especially important with save requests. You should validate client data for data integrity and authorization before storing them to the database.

The JSON payload of a Breeze save request is deserialized by Newtonsoft Json.NET into a “bundle” object representing an entity change-set. The EFContextProvider.SaveChanges method (a) dissects the bundle, (b) prepares the Entity Framework (EF) Context for the save, and (c) calls Context.SaveChanges on your behalf.

Between (a) and (b) it gives you two chances to intervene. You can intercept each entity before the provider adds it to the Context by overriding BeforeSaveEntity. In your override you can approve the entity (return true), exclude the entity silently (return false), or throw an exception that terminates the entire save.

You don’t want too much logic in the repository so, in a bigger app, you probably would treat this override as a dispatcher to type-specific validation classes. But this is a small sample so we validate the TodoItem and TodoList entities right here in the repository.

We validate a TodoList as follows:

private bool BeforeSaveTodoList(TodoList todoList, EntityInfo info)
{
    if (info.EntityState == EntityState.Added)
    {
        todoList.UserId = UserId;
        return true;
    }
    return UserId == todoList.UserId || throwCannotSaveEntityForThisUser();
}

If the TodoList is new, we assign it to the current user. Otherwise (modify and delete), we confirm that the TodoList belongs to the current user and throw an exception it if isn’t. We’re imposing the same logic you’ll find in the PUT, POST, and DELETE methods of the ASP.NET SPA sample’s TodoListController.  Compare for yourself.

Your second opportunity to intervene is the BeforeSaveEntities virtual method which gives you a chance to inspect the entire change-set as a whole. We don’t use that feature in this sample.