Skip to content

petejohanson/angular-hy-res

Repository files navigation

angular-hy-res Build Status

A hypermedia client/library for AngularJS. HAL, Siren, and Link header extensions are included by default, but support for other media types can be added. angular-hy-res is an thin AngularJS wrapper around the core hy-res library.

Support

For any questions, please post to the AngularHyRes Google Group.

Installation

Bower

angular-hy-res is available via Bower. To install:

$ bower install --save angular-hy-res

NPM

angular-hy-res is also published as an node module w/ NPM. To install:

$ npm install --save angular-hy-res

It is recommended to use it along with Browserify or Webpack.

Note: The API is still evolving, so during the 0.0.x series of releases there are no API stability guarantees Those users needed a stable API should set an explicit version in bower.json

Manual

Download the production version or the development version.

In your web page:

<script src="angular.js"></script>
<script src="dist/angular-hy-res-full.min.js"></script>

Documentation

angular-hy-res offers an alternative to the built in AngularJS $resource service that focuses on using hypermedia controls, links (and/or embedded resources) discovered by link relation, to traverse a hypermedia enabled API.

Note: For more documentation, refer to the full API documentation for the core hy-res library.

The core of angular-hy-res is found in the hrCore AngularJs module. To enable it, add that module to your own module definition. In addition, if you want to use the Collection+JSON, HAL, Siren, or Link header integration, you must include the hrCollectionJson, hrHal, hrSiren, or hrLinkHeader modules:

angular.module('myApp', [
    'hrCore',
    'hrCollectionJson',
    'hrHal',
    'hrSiren',
    'hrLinkHeader',
    'hrJson'
  ]);

The hrJson module handles data for simple application/json responses, with no additional hypermedia controls present.

In the future, integration with other hypermedia formats, e.g. Uber, JSON-LD, will be available in their own modules.

hrRoot

Most hypermedia APIs are accessed by fetching the API root from a well known URL, and then following links from there to do any further interactions. The main entry point to angular-hy-res is the hrRoot service, which allows you to fetch a resource for the API root. The easiest way to do this is to inject the root in the $routeProvider:

$routeProvider
  .when('/posts', {
    resolve: {
      root: function(hrRoot) {
        return hrRoot('/api').follow().$promise;
      }
    }
  };

Note: We are using the $promise property of a resource to keep the route from resolving until the root is fully fetched.

hrRoot(url)

Returns a hrWebLink that can be followed to retrieve the root hy-res Resource. See Resource for details on the API available once once you have retrieved the root.

Collection+JSON Extension

By default, the Collection+JSON extension will only process links and embedded resources in responses if the HTTP response Content-Type header equals application/vnd.collection+json. If you have a custom media type that extends Collection+JSON, you can register it with with the hrCollectionJsonExtensionProvider in the mediaTypes array:

angular.module('myModule', ['hrCollectionJson'])
  .config(function(hrCollectionJsonExtensionProvider) {
    hrSirenExtensionProvider.mediaTypes.push('application/vnd.myco.mytype');
  });

Collection+JSON queries are exposed as forms, and can be accessed using Resource#$form or Resource#$forms. For adding items, a form is accessible using the create-form IANA standard link relation.

Collection items can be extracted using the item standard link relation using Resource#$sub or Resource#$subs.

A given embedded item can be edited by using the form with the edit-form standard link relation.

HAL Extension

By default, the HAL extension will only process links an embedded resources in responses if the HTTP response Content-Type header equals application/hal+json. If you have a custom media type that extends HAL, you can register it with with the hrHalExtensionProvider in the mediaTypes array:

angular.module('myModule', ['hrHal'])
  .config(function(hrHalExtensionProvider) {
    hrHalExtensionProvider.mediaTypes.push('application/vnd.myco.mytype');
  });

Siren Extension

By default, the Siren extension will only process links an embedded resources in responses if the HTTP response Content-Type header equals application/vnd.siren+json. If you have a custom media type that extends Siren, you can register it with with the hrSirenExtensionProvider in the mediaTypes array:

angular.module('myModule', ['hrSiren'])
  .config(function(hrSirenExtensionProvider) {
    hrSirenExtensionProvider.mediaTypes.push('application/vnd.myco.mytype');
  });

At this point, the Siren extension includes both the Siren links and the sub-entity embedded links in the set queried by the $link function of hrResource.

Text Extension

The text extension handles responses with text content types, and exposes the text response as a text property on the resolved resource. By default, the text extension will only process responses if the HTTP response Content-Type header equals text/plain. If you would like other text subtypes to be handled, you can register it with with the hrTextExtensionProvider in the subTypes array:

angular.module('myModule', ['hrText'])
  .config(function(hrTextExtensionProvider) {
    hrTextExtensionProvider.subTypes.push('rtf');
  });

Alternately, if you would like the extension to handle all text subtypes, the extension can be configured to be in 'wildcard' mode:

angular.module('myModule', ['hrText'])
  .config(function(hrTextExtensionProvider) {
    hrTextExtensionProvider.wildcard = true;
  });

By doing so, HTTP requests will include a wildcard in the Accept header, e.g. text/*, and the extension will handle any responses with a Content-Type of the text primary media type.

Examples

A complete working example can be found at angular-hy-res-example, which demonstrates the below pagination concept. A public copy is deployed to Heroku at:

https://angular-hy-res-example.herokuapp.com/

For example, given a HAL collection resource that uses the standard link relations next and prev to control paging through the collection, and the item relation for each item in the collection, here is a sample response:

{
    "_links": {
        "self": { "href": "/page/2" },
        "next": { "href": "/page/3" },
        "prev": { "href": "/page/1" }
    },
    "_embedded": {
        "item": [
          {
            "_links": { "self": { "href": "/posts/123" } },
            "title": "MY blog post",
            "tags": [ "blogging", "hypermedia" ]
          }
        ]
    }
}

Then the controller can easily be:

angular.module('angularHyResDocs')
  .controller('ahrdPageCtrl', function(root) {
    $scope.page = root.$followOne('http://api.myserver.com/rel/posts');
    $scope.posts = $scope.page.$followAll('item');
    
    var follow = function(rel) {
      $scope.page = $scope.page.$followOne(rel);
      $scope.posts = $scope.page.$followAll('item');
    };
    
    $scope.next = function() {
      if (!$scope.hasNext()) {
        return;
      }
      
      follow('next');
    };
    
    $scope.prev = function() {
      if (!$scope.hasPrev()) {
        return;
      }
      
      follow('prev');
    };
    
    $scope.hasNext = function() {
      return $scope.page.$has('next');
    };
    
    $scope.hasPrev = function() {
      return $scope.page.$has('prev');
    };
  });

And the view:

<div>
  <ul class="pagination">
    <li>
      <a ng-click="{{prev()}}" ng-class="{disabled: !hasPrev()}">&laquo;</a>
    </li>
    <li>
      <a ng-click="{{next()}}" ng-class="{disabled: !hasNext()}">&raquo;</a>
    </li>
  </ul>
  <ul>
    <li ng-repeat="post in posts">{{post.title}}</li>
  </ul>
</div>

Another complete working example can be found at pollsApiClient, which uses most of library features, such as actions, links, pagination, link following and so on. A public copy is deployed to Heroku at:

https://pollsapiclient.herokuapp.com/