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.
For any questions, please post to the AngularHyRes Google Group.
angular-hy-res is available via Bower. To install:
$ bower install --save angular-hy-res
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
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>
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.
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.
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.
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.
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');
});
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
.
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.
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()}">«</a>
</li>
<li>
<a ng-click="{{next()}}" ng-class="{disabled: !hasNext()}">»</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: