Uncategorized

Using Factories and Promises in Angular to Decouple Directives

Recently the team I’m on has had the opportunity to write an Angular web app to help out our account managers to more easily manage their accounts. When faced with structuring our app, we wanted to create as much isolation between UI components as possible. Thanks to a number of extremely helpful blog posts, we learned about ways to hijack directives to silo off functionality into small, testable pieces. By creating directives with their own controllers and isolate scopes, we can define the entire environment for a directive, allowing it to be placed anywhere with the right data (there are some great articles on this here, here, and here).

(Warning cheesy example ahead) Imagine a book store owner wanting to track the performance of book sales. The components in our app will be fundamentally relying on a Book containing details about a given book

This bookstore manager wants to keep a close track on the latest books. On different pages he may want to show details about a book, some key quotes from a book, or a summary of main characters in a book. For the sake of our increasingly hypothetical example, we may have some components that we want to reuse in different contexts. For our purposes, let’s assume we have a BookDetails and a BookQuotes directive.

directive(‘BookDetails’, function() {
  return {
    restrict: ‘E’,
    scope: {
      book: ‘=‘
    },
    link: function(scope) {
      scope.$watch(‘book’, function(newBook) {
        if (newBook) {
          <call controller function to get relevant details>
        }
      });
    }
  }
});

Since both directives rely on the value book, the controller they live in must manage fetching the book. They must listen on book. Example code may look like this:

<div ng-controller=“parentController”> 
  <book-details book=“book”> </book-details>
  <book-quotes book=“book”> </book-quotes>
</div>

Here we are able to keep our directives separate in clear to read HTML. But our directives aren’t really independent here, they still rely on the parent controller to load the data. This means that wherever we want to insert book details, we have to modify the controller it lives in to handle fetching the data.

However, if we let each directive manage the data it needs, we can remove this pesky dependence on the parent controller. Our book-details would be responsible for fetching its own data. This eliminates our dependency on our parent controller. Which means we can add or remove the book-details controller to any page without having to modify other controller code.

Example using a Book service to fetch data:

<div ng-controller=“parentController”>
 <book-details> </book-details>
 <book-quotes> </book-quotes>
</div>

Now we have a different problem – how to allow each directive to request the data it needs. The naive solution is for each directive to make a request to get the book details it needs:

directive(‘BookDetails’, function(BookService) {
 return {
   restrict: ‘E’,
   scope: {},
  controller: function($scope) {
    $scope.loadBook() {
      BookService.get().then(function(book) {
        $scope.book = book;
      }      
    }
  },
   link: function(scope) {
      scope.loadBook();
   }
 }
});

However, if we have many directives requesting we will make many wasteful requests. We need an effective way to cache the requests so we only have to make the request once. After a few google searches, I found this article.

Maurizio describes a pattern where he uses a factory handing out promises to callers whenever they request a resource. When the resource is received, the factory resolves all promises with the requested resource.
The overall pattern is the following. Each directive requests a resource through this factory and receives a promise for the data. When the factory receives the first request, it calls the server to fetch the desired resource. then, it hands back promises to all callers, so they can wait for the resource to become available. Next, when the data comes back from the server, this factory stores it in its cache and resolves all promises with the data retrieved from the server.

What’s so great about this? It allows us to totally decouple components from each other, creating a consistent pattern for directives to manage the data they need. On our team this pattern has enabled us to iterate more quickly and effectively. We can reorganize pages as needed without spending time on detangling components from each other.

Hopefully this is helpful. We found it very useful for helping decouple components, but I’d love to hear how you tackled this issue! What can we do better?

Discussion

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s