Skip to content

Instantly share code, notes, and snippets.

@3kynox
Created January 6, 2016 09:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save 3kynox/eea690f24ee2096c61a4 to your computer and use it in GitHub Desktop.
Save 3kynox/eea690f24ee2096c61a4 to your computer and use it in GitHub Desktop.
angular ng-admin experimental & initial configuration with api-platform symfony project
'use strict';
// Expand a JSON-LD document.
// Needed to be sure that every docs have the same format
function expandJsonLdDoc($q, $log, url, options) {
return $q(function (resolve, reject) {
jsonld.expand(url, options, function (err, doc) {
if (err) {
$log.log('An error occurred while expanding the JSON-LD document.');
reject();
} else {
resolve(doc);
}
});
});
}
// Find a supported class in the Hydra doc
function findHydraSupportedClass(hydraDoc, supportedClass) {
var supportedClasses = hydraDoc[0]['http://www.w3.org/ns/hydra/core#supportedClass'];
for (var i = 0; i < supportedClasses.length; i++) {
if (supportedClasses[i]['@id'] === supportedClass) {
return supportedClasses[i];
}
}
}
// Retrieve the Hydra documentation and the entrypoint of the API
// This must be done before the AngularJS bootstrap
function fetchHydraDoc(entrypointUrl) {
var initInjector = angular.injector(['ng']);
var $http = initInjector.get('$http');
var $log = initInjector.get('$log');
var $q = initInjector.get('$q');
return $http.get(entrypointUrl).then(function (response) {
return $q(function (resolve, reject) {
var linkHeader = response.headers('Link');
if (!linkHeader) {
reject();
}
var matches = linkHeader.match(/<(.+)>; rel="http\:\/\/www.w3.org\/ns\/hydra\/core#apiDocumentation"/);
if (!matches[1]) {
reject();
}
expandJsonLdDoc($q, $log, response.data, {base: entrypointUrl}).then(function (doc) {
resolve({entrypointUrl: entrypointUrl, entrypointDoc: doc, hydraDocUrl: matches[1]});
}, function () {
reject();
});
});
}, function () {
$log.log('An error occurred while loading the API entrypoint.');
}).then(function (data) {
return $q(function (resolve, reject) {
$http.get(data.hydraDocUrl).then(function (response) {
data.hydraDoc = response.data;
resolve(data);
}, function () {
reject();
});
});
}, function () {
$log.log('An error occurred while loading the Hydra documentation.');
}).then(function (data) {
return $q(function (resolve, reject) {
expandJsonLdDoc($q, $log, data.hydraDoc, {base: data.hydraDocUrl}).then(function(doc) {
data.hydraDoc = doc;
resolve(data);
}, function () {
reject();
});
});
}, function () {
$log.log('An error occurred while loading the Hydra documentation.');
});
}
angular.module('api-platform-hydra-admin', [])
.provider('ApiPlatformHydraAdminConfiguration', ['NgAdminConfigurationProvider', function (nga) {
// Configure ng-admin using data provided by the Hydra documentation
nga.getAdminFromHydraDoc = function (data, admin) {
// Convert a RDF range to a ng-admin type
function getTypeFromRange(range) {
switch (range) {
case 'http://www.w3.org/2001/XMLSchema#dateTime':
return 'datetime';
case 'http://www.w3.org/2001/XMLSchema#integer':
return 'number';
case 'http://www.w3.org/2001/XMLSchema#float':
return 'float';
case 'http://www.w3.org/2001/XMLSchema#boolean':
return 'boolean';
}
return 'string';
}
// Set the title of the API
if (!admin) {
if (data['hydraDoc'][0]['http://www.w3.org/ns/hydra/core#title'][0]['@value']) {
admin = nga.application(data['hydraDoc'][0]['http://www.w3.org/ns/hydra/core#title'][0]['@value']);
} else {
admin = nga.application('Admin');
}
}
var entrypointSupportedClass = findHydraSupportedClass(data.hydraDoc, data.entrypointDoc[0]['@type'][0]);
var idField = nga.field('id');
// Add entities
for(var i = 0; i < entrypointSupportedClass['http://www.w3.org/ns/hydra/core#supportedProperty'].length; i++) {
var property = entrypointSupportedClass['http://www.w3.org/ns/hydra/core#supportedProperty'][i]['http://www.w3.org/ns/hydra/core#property'][0];
var url = data.entrypointDoc[0][property['@id']][0]['@id'];
var entity = nga.entity(new URL(url).pathname.substr(1));
var entrypointSupportedOperations = property['http://www.w3.org/ns/hydra/core#supportedOperation'];
// Add fields
for (var j = 0; j < entrypointSupportedOperations.length; j++) {
var className = entrypointSupportedOperations[j]['http://www.w3.org/ns/hydra/core#returns'][0]['@id'];
if (0 === className.indexOf('http://www.w3.org/ns/hydra/core')) {
continue;
}
var supportedClass = findHydraSupportedClass(data.hydraDoc, className);
var readableFields = [idField];
var writableFields = [];
angular.forEach(supportedClass['http://www.w3.org/ns/hydra/core#supportedProperty'], function (supportedProperty) {
var rdfProperty = supportedProperty['http://www.w3.org/ns/hydra/core#property'][0];
var property = rdfProperty['http://www.w3.org/2000/01/rdf-schema#label'][0]['@value'];
var field = nga.field(property, getTypeFromRange(rdfProperty['http://www.w3.org/2000/01/rdf-schema#range'][0]['@id']));
// Add validation
if (supportedProperty['http://www.w3.org/ns/hydra/core#required'][0]['@value']) {
field.validation({ required: true });
}
// Add placeholder
field.attributes({ placeholder: supportedProperty['http://www.w3.org/ns/hydra/core#description'][0]['@value'] });
// list fields
if (supportedProperty['http://www.w3.org/ns/hydra/core#readable'][0]['@value']) {
readableFields.push(field);
}
// edition and creation fields
if (supportedProperty['http://www.w3.org/ns/hydra/core#writable'][0]['@value']) {
writableFields.push(field);
}
});
entity.listView().fields(readableFields);
entity.creationView().fields(writableFields);
entity.editionView().fields(writableFields);
admin.addEntity(entity);
break;
}
}
return admin;
};
return nga;
}])
.provider('ApiPlatformHydraAdminRestangular', ['RestangularProvider', function (RestangularProvider) {
// Automatically configure Restangular to deal with the Hydra powered API
RestangularProvider.getRestangularProviderForHydra = function (data) {
// The URL of the API endpoint
RestangularProvider.setBaseUrl(data.entrypointUrl);
RestangularProvider.setSelfLinkAbsoluteUrl(false);
// Hydra collections support
RestangularProvider.addResponseInterceptor(function (data, operation, what, url, response) {
// Remove trailing slash to make Restangular working
function populateId(data) {
if (data['@id']) {
var iriParts = data['@id'].split('/');
data.id = iriParts[iriParts.length - 1];
}
}
// Populate href property for the collection
populateId(data);
if ('getList' === operation) {
// TODO the JSON-LD document should be expanded to work with other implementation than API Platform
var collectionResponse = data['hydra:member'];
collectionResponse.metadata = {};
// Put metadata in a property of the collection
angular.forEach(data, function (value, key) {
if ('hydra:member' !== key) {
collectionResponse.metadata[key] = value;
}
});
// Populate href property for all elements of the collection
angular.forEach(collectionResponse, function (value) {
populateId(value);
});
// Pagination
response.totalCount = data['hydra:totalItems'];
return collectionResponse;
}
return data;
});
RestangularProvider.addFullRequestInterceptor(function(element, operation, what, url, headers, params) {
if (operation === 'getList') {
if (1 !== params._page) {
params.page = params._page;
}
params.itemsPerPage = params._perPage;
delete params._page;
delete params._perPage;
}
return { params: params };
});
};
return RestangularProvider;
}]);
'use strict';
/**
* @ngdoc overview
* @name blogApp
* @description
* # blogApp
*
* Main module of the application.
*/
angular
.module('blogApp', [])
;
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width">
<!-- Place favicon.ico and apple-touch-icon.png in the root directory -->
<!-- build:css(.) styles/vendor.css -->
<!-- bower:css -->
<link rel="stylesheet" href="bower_components/bootstrap/dist/css/bootstrap.css" />
<link rel="stylesheet" href="bower_components/ng-admin/build/ng-admin.min.css" />
<!-- endbower -->
<!-- endbuild -->
<!-- build:css(.tmp) styles/main.css -->
<link rel="stylesheet" href="styles/main.css">
<!-- endbuild -->
</head>
<body ng-app="blogApp">
<!--[if lte IE 8]>
<p class="browsehappy">You are using an <strong>outdated</strong> browser. Please <a href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.</p>
<![endif]-->
<div class="container-fluid">
<div ng-include="'views/main.html'" ng-controller="MainCtrl"></div>
<div ui-view></div>
</div>
<!-- Google Analytics: change UA-XXXXX-X to be your site's ID -->
<script>
!function(A,n,g,u,l,a,r){A.GoogleAnalyticsObject=l,A[l]=A[l]||function(){
(A[l].q=A[l].q||[]).push(arguments)},A[l].l=+new Date,a=n.createElement(g),
r=n.getElementsByTagName(g)[0],a.src=u,r.parentNode.insertBefore(a,r)
}(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-XXXXX-X');
ga('send', 'pageview');
</script>
<!-- build:js(.) scripts/vendor.js -->
<!-- bower:js -->
<script src="bower_components/jquery/dist/jquery.js"></script>
<script src="bower_components/angular/angular.js"></script>
<script src="bower_components/bootstrap/dist/js/bootstrap.js"></script>
<script src="bower_components/lodash/lodash.js"></script>
<script src="bower_components/restangular/dist/restangular.js"></script>
<script src="bower_components/es6-promise/promise.js"></script>
<script src="bower_components/jsonld/js/jsonld.js"></script>
<script src="bower_components/ng-admin/build/ng-admin.min.js"></script>
<script src="scripts/admin.js"></script>
<script>
var ENTRYPOINT_URL = 'http://courtier-web-api-platform/';
(function () {
fetchHydraDoc(ENTRYPOINT_URL).then(function (data) {
var blogApp = angular
.module('blogApp', ['ng-admin', 'api-platform-hydra-admin'])
.config(['ApiPlatformHydraAdminRestangularProvider', function (ApiPlatformHydraAdminRestangularProvider) {
var RestangularProvider = ApiPlatformHydraAdminRestangularProvider.getRestangularProviderForHydra(data);
// Extra Restangular configuration
}])
.config(['ApiPlatformHydraAdminConfigurationProvider', function(nga) {
var admin = nga.getAdminFromHydraDoc(data);
// Extra ng-admin configuration
nga.configure(admin);
}]);
angular.element(document).ready(function() {
angular.bootstrap(document, ['blogApp']);
});
});
})();
</script>
<!-- endbower -->
<!-- endbuild -->
<!-- build:js({.tmp,app}) scripts/scripts.js -->
<script src="scripts/app.js"></script>
<script src="scripts/controllers/main.js"></script>
<!-- endbuild -->
</body>
</html>
<!-- empty -->
'use strict';
/**
* @ngdoc function
* @name blogApp.controller:MainCtrl
* @description
* # MainCtrl
* Controller of the blogApp
*/
angular.module('blogApp')
.controller('MainCtrl', function () {
this.awesomeThings = [
'HTML5 Boilerplate',
'AngularJS',
'Karma'
];
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment