Created
January 6, 2016 09:04
-
-
Save 3kynox/eea690f24ee2096c61a4 to your computer and use it in GitHub Desktop.
angular ng-admin experimental & initial configuration with api-platform symfony project
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'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; | |
}]); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'use strict'; | |
/** | |
* @ngdoc overview | |
* @name blogApp | |
* @description | |
* # blogApp | |
* | |
* Main module of the application. | |
*/ | |
angular | |
.module('blogApp', []) | |
; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!-- empty --> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'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