Search code examples
angularjsrestdependency-injectionhateoas

Angular controllers to follow HATEOAS API


I'm trying to implement a Angular app, which will discover links by following the HATEOAS principle.

So let's assume I have some root route #/home, which invokes a HomeController. That home controller, would GET some API entrypoint.

app.controller('home', function(apiRoot) {

   // let's assume apiRoot is some promise-based wrapper on $http
   apiRoot.get().then(function(entryPoint) {
      // what now?
   });
});

Now let's assume that there is some link to products and then a further link to product/{id} and dedicated ProductListController and ProductDetailsController.

There are a number of questions:

1. Is it possible and recommended to avoid explicitly defining routes for product list and product detail?

Example routes would be /products and /product/{:id}. They must not necessarily map to actual URLs.

2. What happens when a product details page is accessed directly? I mean, because the ProductDetailsController has no knowledge about URLs of products, first the entrypoint must be retrieved and then the product list and finally a single product. I've seen examples like

app.controller('productDetails', function(apiRoot, $scope, $routeParams) {

   apiRoot.get().then(function(entryPoint) {
      entrypoint.get('products', function(products) {

         // assumed api to get a templated link
         products.get('product', { 'id': $routeParams.id }).then(function(product) {
            $scope.product = product;
         });
      });
   });
});

There are at least two problem with this approach:

  1. It creates a callback hell though that would be mitigated by returning the next promise in every subsequent then()
  2. Part of such promise chain would be duplicated in many controllers.

What I was thinking would be a way for the HomeController to store promises for each request and share with any other swervice, which needs to follow some link:

app.controller('home', function(apiRoot) {

   // let's assume apiRoot is some promise-based wrapper on $http
   apiRoot.get().then(function(entryPoint) {
      // store entryPoint somewhere
   });
});

app.controller('products', function(entryPoint) {

   entryPoint.get('products').then(function(products) {
      // store products somewhere
   });
});

app.controller('productDetails', function($routeParams, $scope, products) {

   products.get('product', {'id': $routeParams.id}).then(function(product) {
      $scope.product = product;
   });
});

Above I assume I would somehow have Angular inject the entryPoint and products resources.. Is that possible? Or is the $rootScope a sensible place to store them?

3. Would I want to eliminate the string literals used for link names ie. 'products' and 'product' or is it not worth the trouble?


Solution

  • Late response,

    1 & 2. Yes, you get a lot of promises to manage, there is no magic way around that, you will likely have to design your code to to make it easier to read & manage

    • Try doing the logic in services instead of controllers
    • Make sure you break the logic into smaller methods that you can re-use

    You should and can cache the resources that are returned, prior to executing your request again you can check your local cache and decided if you need to refresh it.

    1. Have a look at something like the HAL specification for HATEAOS, it defines how to use links and curies to look up your urls, you will always at some point need a string to look these up, but at least not the whole URL.

    Take a look at https://github.com/jcassee/angular-hypermedia as a solution to these issues.