Introduction
Building a data-rich enterprise application that runs equally well on
desktops, tablets, and other mobile devices is easier said than done. For quite
a while it was nearly impossible for single-page applications (SPA) built in
HTML, CSS, and JavaScript to rival their native-app brethren. But the times,
they are a changing … and fast.
In this article we’ll show you how we’re building JavaScript apps that
can query, save, and maintain entity model data on the client using free and
open source libraries including Angular and Breeze.
The Challenge
In desktop/rich-client applications using .NET (WinForms, WCF, or
Silverlight) or Java (Swing, SWT, or AWT), you can bind the on-screen controls
directly to a rich data model, allowing UI updates to propagate to the entities
and vice-versa. .NET and Java both have rich ORM-based entity managers such
as Entity Framework and Hibernate that allow querying for entities, tracking
changes, and performing unit-of-work saves.
How can we use a similar programming model for JavaScript
applications?
The Solution
Angular and Breeze make it possible.
Angular focuses on presentation. It handles the binding between the UI
and the data model. Changes in HTML elements propagate to the JavaScript
entities and vice versa.
Breeze concentrates on data management. Breeze queries, saves, and
takes care of all data interactions between client and server. Breeze automatically
creates JavaScript model objects (called "entities") that match the shape of
the data coming from the remote service. It adds business rules and the
infrastructure to support validation, change tracking, and navigation to
related entities. Breeze navigation properties automate traversal of the object
graphs that are implicit in a relational model so you can walk the graph from a
customer to its orders and from an order to its line items. Breeze tracks
users’ changes and validates them with rules, some of which may have been
propagated to the client from the server.
If you store data in a database, query and save data as
complex object graphs, and share graphs across multiple views—and want to do it
in JavaScript—there is no better way than with Breeze.
The Todo-Angular sample
We’ll use a Todo application to demonstrate how Angular and Breeze make
building single-page apps much easier than they would be if you tried to write
all of the data binding and management by hand.
Download the samples package from BreezeJS.com. The
Todo-Angular sample, including all source code and prerequisite libraries, can
be found in the /Samples/Todo-Angular/ directory. Open it in Visual
Studio, build it, and run it – with (F5) or without (Ctrl-F5) debugging.
Todo-Angular is a simple CRUD (create, read, update, delete) app. On a
single screen you can create items, update their descriptions, change their
state, or delete them. All of the application logic executes in JavaScript on
the client. The server merely responds to data requests by querying and saving
Todos to a server-side SQL database.
The essential workflow is as follows:
-
Breeze sends a query to the server to retrieve the Todo items. The
server sends a JSON response, which Breeze turns into Todo entities (rich
JavaScript objects) on the client.
- Angular binds the Todo entities to the HTML elements (e.g. textboxes,
checkboxes) and listens for changes.
- When a value is changed on the HTML form (e.g. a user edits the
description of a Todo), Angular sees the change and propagates the new value to
the underlying Todo entity.
- Breeze notices the change to the Todo entity and marks it as modified.
- When a save is triggered, Breeze sends the changed Todo entities to the
server.
Under the hood, the app architecture is Model-View-Control (MVC) which
both Angular and Breeze adhere to nicely.
There’s only one web page, Index.html, whose primary
responsibility is to host the client application. As such, it has CSS at the
top, scripts at the bottom, and a layout section in the middle.
The View
For simplicity, the Todo view—the HTML that displays Todos and accepts
user input—is baked into the HTML as well, within the collapsed
"applicationHost
" <div>
.
A more sophisticated app with multiple client pages would define the
Todo view (and other views) in separate files which client-side app logic would
swap into and out of the "applicationhost
". That’s looking down the road. In
this sample we’re just trying to get a grip on the basics.
Here are a few excerpts of the view HTML positioned over the visuals
they define.
Angular data binding
The "data-ng- ..." attributes are Angular "directives". Directives bind
aspects of the HTML widgets to properties and methods in a controller.
Many of the directive bind to members of the controller itself. Here’s
an excerpt from the controller defined in controller.js:
For those new to Angular, the $scope
is the
data bound object. You could think of it as the ViewModel if that term seems
more familiar.
When Angular creates a new instance of the controller, it
passes an empty $scope object to the controller’s definition function; that’s the
anonymous function in the image above.
The controller adds properties and methods to this $scope,
named to match the declaration in the HTML.
In the following snapshot, we see the "addItem()
" in the
HTML matches the $scope.addItem
method, and the "newTodo
" associated with the
input textbox matches $scope.newTodo
.
The $scope.items
array holds the Todo items that were retrieved by
query. This is bound to a "repeater", the <li>
element, which describes a
template for displaying the individual Todo items.
Some of the Angular directives within that template bind to properties
of an individual Todo item entity rather than the controller. For example, the
checkbox is bound to the item’s IsDone
property while the label is bound
to the item’s Description
property.
Custom directives
One of Angular’s strengths is the ability to extend its
native binding directives with custom directives to meet application-specific
needs. This module defines some custom Angular directives that listen to focus
changes.
Here we see the custom "onBlur
" directive, defined within
the controller.js file, applied to the item description textbox
("data-on-blur").
When the user leaves the textbox after editing the
description, the textbox loses focus. The directive detects the "blur" and
calls the "completeEdit
" method on the controller.
From a broader perspective, we see that the controller manages the
application workflow by mediating between the view and the model layers. It
relies on Angular to get to the screen and delegates to the dataservice
when it needs to acquire and manipulate Todos items.
The dataservice
handles all entity creation, queries, and saves.
The methods often return promises
from the asynchronous operations so that the controller can respond
appropriately later when these operations complete.
Breeze itself turns controller commands into HTTP requests to a single
ASP.NET Web API controller on the server. That controller delegates to a Breeze.NET
component, EFContextProvider
which handles interactions with the Entity
Framework and the application’s "Code First" TodoItem model. The raw data are
stored in a SQL Server CE database.
A slight Breeze
The Breeze documentation, live tutorial, and samples are the best ways to learn
what Breeze development is about. Let’s look at just one of the dataservice.js
methods to whet your appetite.
The controller passes in a flag to tell the dataservice
whether it should return all Todos—including the archived Todos—or just the
active Todos. The flag comes from the controller’s isArchived
property
which is bound to the "Show archived" checkbox.
We begin by creating a Breeze query object that targets a
remote service method called "Todos". If we simply executed that query now, the
remote service method would return every Todo item in the database.
But the developer wrote that service method so it will
respond to an OData-style query. So, on the client, we add an orderBy
clause telling the service to sort the query results, by creation date. The
sorting will occur on the data tier, before the data come over the wire.
If the user only wants to see active Todos (i.e., the
includeArchived
flag is false), we need to exclude the archived Todos by adding
a filter. The "where" clause adds that filter to the query. Now when we execute
the query, the results will consist of just the active Todos, sorted by
creation date.
This query building strategy and syntax should remind you of
LINQ.
In the final statement, a Breeze EntityManager
executes the query. The query operation is asynchronous and returns a promise
to the controller: a promise to notify the controller when the query succeeds,
or fails. Here’s how the controller called the dataservice and handled the
promise.
After a pause, the remote service returns Todo item data to
the client as JSON. Breeze converts that JSON into Breeze entities which are
equipped with validation, change tracking, and other capabilities of the Breeze
system. These entities are merged into the EntityManager
’s cache before
they are returned to the controller as query results.
When the user makes changes on screen, the corresponding
Todo entity in cache changes to a "Modified" state. The application is designed
to save immediately so the controller tells the dataservice which ultimately
delegates to a call upon the EntityManager
’s saveChanges
method:
It’s that simple. The manager collects all cached entities
with unsaved changes and sends them to the remote service as a change-set. The
remote service, unpacks the change-set, applies the appropriate business logic,
and stores it to the database as a single transaction. The business logic (very
little in this sample) is yours; the rest of the plumbing is handled by Breeze.
We should mention that the dataservice.js in this sample is
almost identical to the dataservice for a parallel Todo sample that uses
Knockout rather than Angular. It takes a single line of configuration to switch
between Angular, Knockout, Backbone or some other model library of your choice.
Breeze itself has no intrinsic affinity for any particular
JavaScript data binding library.
That’s the Todo-Angular app in a nutshell.
The JavaScript inventory
The Todo-Angular client depends on five free and open-source third-party
libraries and three application scripts.
Third-party libraries
All third party libraries are in the Scripts folder.
Angular.js
Angular handles the
plumbing – the data-binding, event monitoring, and DOM manipulation for the
presentation layer.
Breeze.js
Breeze takes care of
the "Model" concerns in the MVC triad. It maintains a client side cache of
persistent model objects consisting of newly created entities and entities
materialized by a query. Breeze handles your query and save operations.
jQuery.js
There’s not a lot of jQuery
in this particular sample. Breeze is relying on jQuery.ajax for communication
with the back-end service.
Q.js
Q assists in
managing asynchronous operations through CommonJs promises.
toastr.js
Toastr displays
process and error notifications in "toast" windows that float up from the lower
right.
Client-side scripts
The client-side application scripts are in the Scripts/app
folder.
controller.js
The controller.js defines two Angular modules,
TodoMain
and TodoCtrl
. Custom directives are defined in TodoMain
. TodoCtrl
is
the controller module that manages the view.
dataservice.js
The dataservice.js provides the modeling and
client-side data access layer for the application. It leans heavily on Breeze.
logger.js
The logger.js is merely an abstraction wrapped around the 3rd
party toastr library. You could rip toastr out and log to console if you
prefer.
What about the server?
Both AngularJS and BreezeJS are pure JavaScript libraries. Neither
requires .NET, Visual Studio, Entity Framework, or ASP.NET, and you can write
the server however you’d like.
You do need a server of some kind to deliver the web assets and data
services. We used .NET for this example because it’s quick and effective for
folks already building on the Microsoft stack. It helps that Breeze ships with
components to ease development of Web API and Entity Framework back-ends.
This Todo-Angular server is an ASP.NET Web Application. It hosts all
the client-side assets as well as an ASP.NET MVC4 Web API service that queries and
saves to a SQL Server database with the help of an Entity Framework Code First
model. This article concentrates on client-side development so will just leave
it at that for now.
Learn more
In addition to the core docs and API, you
can find detailed information specific to the Todo-Angular sample, including:
About
Breeze is actively developed by IdeaBlade. Follow @BreezeJS on Twitter and Like us on Facebook.