Promises in AngularJS

In this tutorial, we will keep an eye on “Promises in AngularJS

We first looked at fetching categories from a server by using $http directly inarticleCtrl. For this to work, we would have to undo some of the decoupling we previously achieved by having calculateCategoryPercentage as a factory service with a dependency on availableCategories.

Let’s now return to that and see if we can extract $http from articleCtrl and use it in availableCategories. If we can achieve that, articleCtrl andcalculateCategoryPercentage can become oblivious to how the available categories are obtained. This is a more advanced technique that will help keep code flexible as our applications grow.

We glossed over it in our simple illustration of $http, but the get method made the HTTP request (which is inherently asynchronous) and immediately returned a Promise object. A Promise object represents the future value of an asynchronous operation. If the operation succeeds, then the Promise is ‘resolved’; if it fails, then the Promise is ‘rejected’. A Promise object exposes several methods, one of whichthen allows us to register a resolution and/or rejection handler. In our simple usage, we just registered a resolution handler as follows:

Promises can, without doubt, get reasonably complicated. If you want to learn more, I suggest studying the readme for the Q library. AngularJS implements a lightweight version of the Q library.

Now that we’ve briefly introduced Promises, let’s look at how we can use them to get our decoupling back. In the following snippets, I’ve added numbered comments, which we’ll discuss in detail shortly.

Edit controllers.js as follows:

Edit services.js as follows:

In the above snippets, the numbered comments relate to the following points:

  1. articleCtrl depends on calculateCategoryPercentage which depends onavailableCategories. The Promise object returned from$http.get('/categpries').then is registered as availableCategories. Note that availableCategories has been made a factory service so that it, too, can have a dependency injected (namely, $http).
  2. calculateCategoryPercentage is next in the dependency chain, so the functioncalculate is registered.
  3. articleCtrl runs as the last step in the dependency chain. It callscalculateCategoryPercentage each time its articles change (via $scope.$watch). A Promise object is returned, and articleCtrl assigns a resolution handler.
  4. A resolution handler is assigned to the availableCategories Promise object. Assigning resolution handlers via then returns another Promise object, which allows for chained resolution.
  5. availableCategories is resolved (i.e., a response is received from the server), and the category percentage is calculated and returned.
  6. The chained resolution set in step 4 allows articleCtrl to set the category percentage as a $scope property.

You may wonder about the benefit of this approach over the simpler use of $httpdirect in articleCtrl we had previously. In both approaches, we have had to change how calculateCategoryPercentage is used in articleCtrl. In this approach, the change has been to work with Promises. Promises are a very general API. For example, in the future our application could first look in the browser’s local storage for categories before resorting to an HTTP server call. The Promise API thatarticleCtrl works with wouldn’t change one bit, but behind the scenes, obtaining the categories would be more involved. With Promises, articleCtrl has no insight into how the categories are obtained for the calculation, just that somehow they are.

$resource

Until now, the initial articles in our blog admin application have been hard-coded in articleCtrl. This clearly isn’t the most flexible application around; as such, the requirements have changed yet again.

We’re now asked to provide a means of retrieving and creating articles stored on a server. The server has provided us a RESTful API for interacting with articles. Sending an HTTP GET request to the URL ‘/articles’ will return an array of articles, while sending an HTTP POST request will create and return a new article. $resourceis another additional module and is perfect for working with this type of API.

Download angular-resource.js and add another script reference in index.html in the following order:

As with $http previously, the simplest way to get up and running with $resource is to use it directly in articleCtrl. We could encapsulate $resource in another factory service so that articleCtrl isn’t aware of how articles are retrieved and created. For our purposes, the first approach allows us to focus on the detail of using $resource, but in a larger real-world application, I would certainly consider the latter approach.

Edit app.js as follows:

The change in the above snippet is a simple one: we’ve just added an additional dependency for the ‘udemyAdmin’ module, namely ‘ngResource’.

Edit controllers.js as follows:

In the above snippet, $resource is declared as a dependency of articleCtrl and subsequently injected in. We create a resource object Article by invoking $resourcewith our URL.

We then invoke Article.query to retrieve an array of articles from the server. This results in an HTTP GET request to ‘/articles’. As this is an asynchronous operation, it does not block the application waiting for the server to respond, but what value would be useful to return in the meantime? $resource immediately returns an empty array, which will be filled when the server responds. This is a neat trick, as combined with setting the array as a property on $scope, any binding we make in our HTML will automatically update.

Continuous

We haven’t looked at the contents of index.html for a while, but the usage of ng-repeat doesn’t need to change, even though we’ve switched to $resource and$scope.articles is, at first, an empty array.

Returning to the snippet of articleCtrl above, when $scope.addArticle is invoked we now create a new instance of Article with $scope.newTitle as its title. We then invoke$save resulting in an HTTP POST request to ‘/articles’. A Promise object is returned, which is resolved by the server responding with an HTTP 200 status code and body. A instance of Article is created from the server response for us, and we simply push it onto $scope.articles. The usage of $scope.addArticle and$scope.newTitle in index.html does not need to change.

$httpBackend again

As $resource is an extension of $http, we can also use $httpBackend to configure how requests to the URL ‘/articles’ should be handled.

Edit app.js as follows:

In the above snippet, we’ve simply moved the hard-coded array of articles fromarticleCtrl and returned it whenever an HTTP GET request is made to ‘/articles’. An HTTP POST request to ‘/articles’ pushes another article onto the array, meaning that any subsequent GET requests would include it.