Breeze and AngularJS work well together. They work better together when you configure Breeze to use AngularJS promises ($q) and AngularJS’s ajax component ($http).
The Breeze AngularJS Service is a Breeze “bridge” adapter that performs this configuration for you in an “ngNatural” way.
Breeze+AngularJS is great for modern browsers (ECMAScript 5+) that support Object.defineProperty. If your app must run in IE8 or earlier, the Breeze/AngularJS combination is not for you. You might consider Breeze+Durandal (Knockout).
acquire the breeze.bridge.angular.js JavaScript file in one of the ways described below
This breeze.bridge.angular.js used to be a Breeze Labs file named breeze.angular.js. It was moved to Breeze core and renamed in January, 2015.
The “breeze.angular” service is included among the adapters in the breeze-client bower package.
bower install breeze-client
Now load it in your web page html after breeze itself.
<script src="bower_components/breeze-client/adapters/breeze.bridge.angular.js"></script>
The “breeze.angular” service is included among the adapters in the breeze-client npm package.
The “breeze-client” npm package becomes available with Breeze release v.1.5.3.
npm install breeze-client
Now load it in your web page html after breeze itself.
<script src="node_modules/breeze-client/adapters/breeze.bridge.angular.js"></script>
You can download the raw JavaScript file from github. Put it wherever you like. Load it in your web page html after breeze itself.
Visual Studio users can install it with NuGet:
Install-Package Breeze.Angular
Now load it in your web page html after breeze itself.
<script src="Scripts/breeze.bridge.angular.js"></script>
The nuget package depends on breeze.client and the breeze.angular.directives package which provides the “zValidate” validation directive. You may be able to install everything you need for Breeze+AngularJS client development with this one package.
Example #1: Configure when your application boots
1
2
3
4
5
6
7
var app = angular.module('app', [
// ... other dependencies ...
'breeze.angular' // the breeze service module
]);
// Ensure that breeze is minimally configured by loading it when the app runs
app.run(['breeze', function (breeze) { }]); // doing nothing at the moment
Example #2: Configure upon your first use of Breeze
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
var app = angular.module('app', [
// ... other dependencies ...
'breeze.angular' // the breeze service module
]);
// Any first use of breeze would likely involve the breeze.EntityManager.
// Apps often combine EntityManager and breeze configuration in a "factory".
// This 'entityManagerFactory' creates a new EntityManager
// configured for a specific remote service.
angular.module('app')
.factory('entityManagerFactory', ['breeze', emFactory]);
function emFactory(breeze) {
// Convert properties between server-side PascalCase and client-side camelCase
breeze.NamingConvention.camelCase.setAsDefault();
// Identify the endpoint for the remote data service
var serviceRoot = window.location.protocol + '//' + window.location.host + '/';
var serviceName = serviceRoot + 'breeze/breeze'; // breeze Web API controller
// the "factory" services exposes two members
var factory = {
newManager: function() {return new breeze.EntityManager(serviceName);},
serviceName: serviceName
};
return factory;
}
The Breeze AngularJS Service is not clairvoyant. It can’t configure Breeze for everything your app requires. The second example illustrates configuration of the NamingConvention and the remote service endpoint (the serviceName
), both specific to your application.
The ‘breeze’ service that AngularJS injects is Breeze itself, identical to window.breeze
. Whether you use that service object or refer to the global breeze
object is a matter of style.
The “Breeze AngularJS Service” simply configures Breeze to use
$q
service for promises$http
service for ajax callsThe balance of this documentation provides more details about promises and the ajax service.
A promise is a pledge to tell you when an asynchronous activity completes.
Breeze and AngularJS rely on promises to manage chaining of asynchronous method calls such as a sequence of data queries.
Every Breeze async method returns a promise object. Initially the asynchronous activity is incomplete and the promise object is “unfullfilled”. Your code continues without knowing the outcome of that activity. The promise object has a > then() method. You supply success and failure callbacks as parameters to the > then(). When the asynchronous activity completes, the promise is “fullfilled” and it invokes either your success or your failure callback as appropriate.
Read more about “Thenable Promises” from the author of the Q.js library, Kris Kowal. The AngularJS $q
implementation adheres to his description in all essential respects.
Out of the box, a Breeze asynchronous method returns a Q.js promise, not an AngularJS $q promise. Breeze also assumes that you included the Q.js library in your client stack.
While the Q.js default makes good sense in other environments, it is not AngularJS friendly. First you have to load the Q library. Then you’ll find that you often have to convert a Q promise to a $q
promise because many AngularJS components don’t understand a Q promise. Because the AngularJS dirty-checking “Digest” cycle knows nothing of Q, you’ll probably have to call > $scope.$apply. Finally, it’s extremely difficult to test with ng-mocks when you have a mix of $q
and Q promises.
AngularJS developers should switch to $q
promises and this Breeze AngularJS Service does that for you automatically.
There’s nothing to it. Breeze async methods now return AngularJS $q
promises. Append promise callbacks to those promises per the $q
API.
1
2
3
var promise = entityManager
.executeQuery(query)
.then(successCallback, failCallback); // not preferred; see next.
What if one of the callbacks throws an exception? Per the specification, if either the successCallback
or failCallback
throws an exception, the promise returned from then(...)
is rejected. Don’t expect a failed successCallback
to propagate its error to the sibling failCallback
.
Because the successCallback
is often fragile, especially in tests, we often move the failCallback
to a separate then(null, failCallback)
so that it can catch failures either of the original promise or of the “success path” promise.
The
$q
promise sports a “sugar” method,.catch(failCallback)
, which is the same as.then(null, failCallback)
.
You may also need cleanup logic that should run whether the original promise succeeds or fails.
While you could append
.then(wrapUp, wrapUp)
, we prefer another bit of sugar,.finally(wrapUp)
.
Putting these thoughts together we might write something like this:
1
2
3
4
5
var promise = entityManager
.executeQuery(query)
.then(successCallback)
.catch(failCallback)
.finally(wrapUp);
We encourage you to review the $q
promises documentation for details.
The Breeze EntityManager
makes HTTP calls to a remote server via an “ajax” adapter. While Breeze ships with both a ‘jQuery’ and an ‘angular’ ajax adapter, it defaults to the ‘jQuery’ adapter which wraps jquery.ajax
. This Breeze AngularJS Service re-configures breeze to use the ‘angular’ adapter which wraps $http
, ensuring that Breeze receives the specific $http
service instance that AngularJS injects into your app module.
Speaking of service instances …
There is a nuance you may discover in extraordinary circumstances: AngularJS creates a new $q
and a new $http
for each application module.
Rare is the application that has multiple app modules. But if you did have multiple app modules, each would have its own $q
and $http
instance.
Breeze expects exactly one promise and ajax implementation across the entire application. That might become a problem if you toggled between multiple app modules. You could workaround it by switching the Breeze promise and ajax implementations every time you switch app modules. The specifics of this technique are beyond the scope of this topic.
You’re more likely to become aware of multiple $q
and $http
instances during testing. In fact, you can count on getting a new instance for each and every test (known as a “spec” in Jasmine).
Fortunately, you get a new instance of the app module too. So when your app module and services load under test, they create a fresh instance of the Breeze AngularJS service at the same time … and that new instance will configure Breeze with the current $q
and $http
services for each executing test (or “spec”).
Here’s an example of a Jasmine “beforeEach” test setup:
1
2
3
4
beforeEach(function () {
module('app'); // new instance of the 'app' module
inject(function(breeze){ }); // just to be sure.
})