Breeze Angular Q-Promises

A BreezeJS asynchronous method returns a promise, typically a promise to deliver some data from a remote service (or an error if the service request fails).

By default, Breeze asynchronous methods return Q.js promises not AngularJS $q promises. You have to include the Q.js library in your client stack.

You can switch to Angular's $q promises by including this Angular module in your application and telling Breeze which $q instance to use. Then you can remove the Q.js library from your application.

This breeze.angular.q library has been deprecated. It is superseded by the Breeze Angular Service which more cleanly configures breeze for Angular development.

The Breeze Angular Service tells Breeze to use Angular's $q for promises and to use Angular's $httpfor ajax calls.

The breeze.angular.q library and the documentation below on how to install and use it will be retained until the end of March 2014. Then this documentation will be removed and the library will no longer be maintained. As always, you can retrieve the frozen copy from github.

Install this module

Visual Studio developers can install the module via NuGet. Or you can download a copy of breeze.angular.q.js from GitHub. Then follow these steps:

  1. Load the script in your index.html after loading BreezeJS itself.

  2. Make your app module depend upon the 'breeze.angular.q' module

  3. Before calling any breeze async function, call use$q($q) when your app module runs.

For example:

var app = angular.module('app', [
   // ... other dependencies ...
   'breeze.angular.q' // tells breeze to use $q instead of Q.js
]);

app.run(['$q','use$q', function ($q, use$q) {
       use$q($q);
}]);

You no longer need Q.js. You can remove it from your index.html and delete the file from your application.

Usage

There's nothing to it. Breeze async methods now return Angular $q promises. Append promise callbacks to those promises per the $q API.

var promise = entityManager
       .executeQuery(query)
       .then(successCallback, failCallback);

Exceptions

What if one of the callbacks throws an exception? Per the specification, if either 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(...) so that it can catch failures either of the original promise or of the "success path" promise.

You may also have cleanup logic that should run whether the original promise succeeds or fails.

Putting these thoughts together we might write something like this:

var promise = entityManager
       .executeQuery(query)
       .then(successCallback)
       .catch(failCallback)     // same as 'then(null, failCallback)'
       .finally(finalCallback); // sort of like 'then(finalCallback, finalCallback)'

We encourage you to review the $q promises documentation for details.

Testing and multiple app modules

Remember that each Angular application module gets its own Dependency Injection container ($inject) with its own $q singleton instance. That's why we wait until app.run() to tell Breeze which $q to use.

At any particular moment, Breeze has exactly one active promise service (AKA a "Q service"). If you have two or more application modules, each with its own $q service, you'll have to switch between them as you switch modules by calling use$q($q) after each switch.

When you test your Angular application with angular-mocks, you get a new $q service before each test run. Breeze needs that new $q service instance too ... and will get it as long as you include your app module in your test setup as seen in this example a Jasmine test:

beforeEach(module('app')); // app.run updates Breeze with the test instance of $q

Deprecating to$q

This library replaces our previously recommended breeze.to$q.js library from Breeze Labs.

We have deprecated breeze.to$q.js and are in the process of removing it from the Breeze Labs.

To give you time to complete the migration, breeze.angular.q.js adds a to$q method to every promise returned by a Breeze asynchronous method. Your "to$q" code will continue to work even when Q.js is no longer present.

The Breeze Angular Service does not retain this backwards-compatibility feature.