From b6749a69f498db801e9e12268cfd3a1cd5ebc316 Mon Sep 17 00:00:00 2001 From: Francois Zaninotto Date: Sat, 27 Jun 2015 22:38:08 +0200 Subject: [PATCH 01/19] Prepare move of mapEntry from datastore to view --- src/javascripts/ng-admin/Crud/routing.js | 34 +++++------------------- 1 file changed, 7 insertions(+), 27 deletions(-) diff --git a/src/javascripts/ng-admin/Crud/routing.js b/src/javascripts/ng-admin/Crud/routing.js index 8c183154..15636dc0 100644 --- a/src/javascripts/ng-admin/Crud/routing.js +++ b/src/javascripts/ng-admin/Crud/routing.js @@ -138,12 +138,7 @@ function routing($stateProvider) { return true; }], entries: ['dataStore', 'view', 'response', 'referencedEntries', function (dataStore, view, response, referencedEntries) { - var entries = dataStore.mapEntries( - view.entity.name(), - view.identifier(), - view.getFields(), - response.data - ); + var entries = view.mapEntries(response.data); // shortcut to diplay collection of entry with included referenced values dataStore.fillReferencesValuesFromCollection(entries, view.getReferences(), true); @@ -182,13 +177,8 @@ function routing($stateProvider) { rawEntry: ['$stateParams', 'ReadQueries', 'view', function ($stateParams, ReadQueries, view) { return ReadQueries.getOne(view.getEntity(), view.type, $stateParams.id, view.identifier(), view.getUrl()); }], - entry: ['dataStore', 'view', 'rawEntry', function(dataStore, view, rawEntry) { - return dataStore.mapEntry( - view.entity.name(), - view.identifier(), - view.getFields(), - rawEntry - ); + entry: ['view', 'rawEntry', function(view, rawEntry) { + return view.mapEntry(rawEntry); }], nonOptimizedReferencedData: ['ReadQueries', 'view', 'entry', function (ReadQueries, view, entry) { return ReadQueries.getFilteredReferenceData(view.getNonOptimizedReferences(), [entry.values]); @@ -324,13 +314,8 @@ function routing($stateProvider) { rawEntry: ['$stateParams', 'ReadQueries', 'view', function ($stateParams, ReadQueries, view) { return ReadQueries.getOne(view.getEntity(), view.type, $stateParams.id, view.identifier(), view.getUrl()); }], - entry: ['dataStore', 'view', 'rawEntry', function(dataStore, view, rawEntry) { - return dataStore.mapEntry( - view.entity.name(), - view.identifier(), - view.getFields(), - rawEntry - ); + entry: ['view', 'rawEntry', function(view, rawEntry) { + return view.mapEntry(rawEntry); }], nonOptimizedReferencedData: ['ReadQueries', 'view', 'entry', function (ReadQueries, view, entry) { return ReadQueries.getFilteredReferenceData(view.getNonOptimizedReferences(), [entry.values]); @@ -442,13 +427,8 @@ function routing($stateProvider) { rawEntry: ['$stateParams', 'ReadQueries', 'view', function ($stateParams, ReadQueries, view) { return ReadQueries.getOne(view.getEntity(), view.type, $stateParams.id, view.identifier(), view.getUrl()); }], - entry: ['dataStore', 'view', 'rawEntry', function(dataStore, view, rawEntry) { - return dataStore.mapEntry( - view.entity.name(), - view.identifier(), - view.getFields(), - rawEntry - ); + entry: ['view', 'rawEntry', function(view, rawEntry) { + return view.mapEntry(rawEntry); }], } }); From 804e1f21b72a6a1db9177bac332ac4a90b45e790 Mon Sep 17 00:00:00 2001 From: Francois Zaninotto Date: Sun, 28 Jun 2015 00:17:33 +0200 Subject: [PATCH 02/19] Add transform() to README --- README.md | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 9136270c..a72de1e8 100644 --- a/README.md +++ b/README.md @@ -433,7 +433,7 @@ Tell if the value is a link in the list view. Default to true for the identifier Define the route for a link in the list view, i.e. `isDetailLink` of the field is true. The default is `edit`, hence the link points to the edition view. The other option is `show` to point to the show view. * `map(function)` -Define a custom function to transform the value. It receive the value and the corresponding entry. Works in list, edit views and references. +Define a custom function to transform the value received from the API response to the value displayed in the admin. This function receives 2 parameters: the value to transform, and the corresponding entry. Works in list, edit views and references. nga.field('characters') .map(function truncate(value, entry) { @@ -446,6 +446,24 @@ Define a custom function to transform the value. It receive the value and the co .map(stripTags) .map(truncate); +* `transform(function)` +Define a custom function to transform the value displayed in the admin back to the one expected by the API. This function receives 2 parameters: the value to transform, and the corresponding entry. Used in edit view only. Use it in conjunction with `map()` to ease the conversion between the API response format and the format you want displayed on screen. + + // API + // map() v ^ tranform() + // Entry︎ + // + // The API provides and expects last names in all caps, e.g. 'DOE' + // The admin should display them with capitalized last names, e.g 'Doe' + nga.field('last_name') + .map(function capitalize(value, entry) { + return value.substr(0,1).toUpperCase() + value.substr(1).toLowerCase() + }) + .transform(function allCaps(value, entry) { + // the API expects upper case last names + return value.toUpperCase(); + }); + * `validation(object)` Tell how to validate the view - `required`: boolean From 1510029765143feb11b452aa81d7a0d4e24215c8 Mon Sep 17 00:00:00 2001 From: Francois Zaninotto Date: Fri, 3 Jul 2015 19:16:42 +0200 Subject: [PATCH 03/19] Add nested entries --- examples/blog/config.js | 6 +++-- examples/blog/stub-server.json | 43 +++++++++++++++++++++++++--------- 2 files changed, 36 insertions(+), 13 deletions(-) diff --git a/examples/blog/config.js b/examples/blog/config.js index 05f9d881..f3dba621 100644 --- a/examples/blog/config.js +++ b/examples/blog/config.js @@ -155,7 +155,8 @@ .fields([ nga.field('created_at', 'date') .label('Posted'), - nga.field('author'), + nga.field('author.name') + .label('Author'), nga.field('body', 'wysiwyg') .stripTags(true) .map(truncate), @@ -186,7 +187,8 @@ nga.field('created_at', 'date') .label('Posted') .defaultValue(new Date()), // preset fields in creation view with defaultValue - nga.field('author'), + nga.field('author.name') + .label('Author'), nga.field('body', 'wysiwyg'), nga.field('post_id', 'reference') .label('Post') diff --git a/examples/blog/stub-server.json b/examples/blog/stub-server.json index 368f5b00..e110bce0 100644 --- a/examples/blog/stub-server.json +++ b/examples/blog/stub-server.json @@ -183,78 +183,99 @@ "comments": [ { "id": 1, - "author": "Sigurd O'Conner", + "author": {}, "post_id": 6, "body": "Queen, tossing her head through the wood. 'If it had lost something; and she felt sure it.", "created_at": "2012-08-02" }, { "id": 2, - "author": "Kiley Pouros", + "author": { + "name": "Kiley Pouros", + "email": "kiley@gmail.com" + }, "post_id": 9, "body": "White Rabbit: it was indeed: she was out of the ground--and I should frighten them out of its right paw round, 'lives a March Hare. 'Sixteenth,'.", "created_at": "2012-08-08" }, { "id": 3, - "author": "Justina Hegmann", + "author": { + "name": "Justina Hegmann" + }, "post_id": 3, "body": "I'm not Ada,' she said, 'and see whether it's marked \"poison\" or.", "created_at": "2012-08-02" }, { "id": 4, - "author": "Ms. Brionna Smitham MD", + "author": { + "name": "Ms. Brionna Smitham MD" + }, "post_id": 6, "body": "Dormouse. 'Fourteenth of March, I think I can say.' This was such a noise inside, no one else seemed inclined.", "created_at": "2014-09-24" }, { "id": 5, - "author": "Edmond Schulist", + "author": { + "name": "Edmond Schulist" + }, "post_id": 1, "body": "I ought to tell me your history, you know,' the Hatter and the happy summer days. THE.", "created_at": "2012-08-07" }, { "id": 6, - "author": "Danny Greenholt", + "author": { + "name": "Danny Greenholt" + }, "post_id": 6, "body": "Duchess asked, with another hedgehog, which seemed to be lost: away went Alice after it, never once considering how in the other. In the very tones of.", "created_at": "2012-08-09" }, { "id": 7, - "author": "Luciano Berge", + "author": { + "name": "Luciano Berge" + }, "post_id": 5, "body": "While the Panther were sharing a pie--' [later editions continued as follows.", "created_at": "2012-09-06" }, { "id": 8, - "author": "Annamarie Mayer", + "author": { + "name": "Annamarie Mayer" + }, "post_id": 5, "body": "I tell you, you coward!' and at once and put it more clearly,' Alice.", "created_at": "2012-10-03" }, { "id": 9, - "author": "Breanna Gibson", + "author": { + "name": "Breanna Gibson" + }, "post_id": 2, "body": "THAT. Then again--\"BEFORE SHE HAD THIS FIT--\" you never tasted an egg!' 'I HAVE tasted eggs, certainly,' said Alice, as she spoke. Alice did not like to have it.", "created_at": "2012-11-06" }, { "id": 10, + "author": { + "name": "Logan Schowalter" + }, "post_id": 3, - "author": "Logan Schowalter", "body": "I'd been the whiting,' said the Hatter, it woke up again with a T!' said the Gryphon. '--you advance twice--' 'Each with a growl, And concluded the banquet--] 'What IS the fun?' said.", "created_at": "2012-12-07" }, { "id": 11, + "author": { + "name": "Logan Schowalter" + }, "post_id": 1, - "author": "Logan Schowalter", "body": "I don't want to be?' it asked. 'Oh, I'm not Ada,' she said, 'and see whether it's marked \"poison\" or not'; for she had asked it aloud; and in despair she put her hand on the end of the.", "created_at": "2012-08-05" } From ea57757ded294e367a39ef0236b41fb4a32ab79b Mon Sep 17 00:00:00 2001 From: Francois Zaninotto Date: Tue, 7 Jul 2015 09:45:45 +0200 Subject: [PATCH 04/19] Use Entry mapping capacities from Entry, not DataStore --- src/javascripts/ng-admin/Crud/routing.js | 90 ++++++++++++++---------- 1 file changed, 51 insertions(+), 39 deletions(-) diff --git a/src/javascripts/ng-admin/Crud/routing.js b/src/javascripts/ng-admin/Crud/routing.js index 15636dc0..9c590fac 100644 --- a/src/javascripts/ng-admin/Crud/routing.js +++ b/src/javascripts/ng-admin/Crud/routing.js @@ -41,6 +41,13 @@ function dataStoreProvider() { }]; } + +function entryConstructorProvider() { + return ['AdminDescription', function (AdminDescription) { + return AdminDescription.getEntryConstructor(); + }]; +} + function routing($stateProvider) { $stateProvider @@ -56,6 +63,7 @@ function routing($stateProvider) { templateProvider: templateProvider('ListView', listLayoutTemplate), resolve: { dataStore: dataStoreProvider(), + Entry: entryConstructorProvider(), view: viewProvider('ListView'), filterData: ['ReadQueries', 'view', function (ReadQueries, view) { return ReadQueries.getAllReferencedData(view.getFilterReferences(false)); @@ -65,11 +73,11 @@ function routing($stateProvider) { var filterEntries; for (var name in filterData) { - filterEntries = dataStore.mapEntries( - filters[name].targetEntity().name(), - filters[name].targetEntity().identifier(), + filterEntries = Entry.createArrayFromRest( + filterData[name], [filters[name].targetField()], - filterData[name] + filters[name].targetEntity().name(), + filters[name].targetEntity().identifier().name() ); dataStore.setEntries( @@ -98,6 +106,7 @@ function routing($stateProvider) { template: listTemplate, resolve: { dataStore: dataStoreProvider(), + Entry: entryConstructorProvider(), view: viewProvider('ListView'), response: ['$stateParams', 'ReadQueries', 'view', function ($stateParams, ReadQueries, view) { var page = $stateParams.page, @@ -116,17 +125,17 @@ function routing($stateProvider) { optimizedReferencedData: ['ReadQueries', 'view', 'response', function (ReadQueries, view, response) { return ReadQueries.getOptimizedReferencedData(view.getOptimizedReferences(), response.data); }], - referencedEntries: ['dataStore', 'view', 'nonOptimizedReferencedData', 'optimizedReferencedData', function (dataStore, view, nonOptimizedReferencedData, optimizedReferencedData) { + referencedEntries: ['dataStore', 'Entry', 'view', 'nonOptimizedReferencedData', 'optimizedReferencedData', function (dataStore, Entry, view, nonOptimizedReferencedData, optimizedReferencedData) { var references = view.getReferences(), referencedData = angular.extend(nonOptimizedReferencedData, optimizedReferencedData), referencedEntries; for (var name in referencedData) { - referencedEntries = dataStore.mapEntries( - references[name].targetEntity().name(), - references[name].targetEntity().identifier(), + referencedEntries = Entry.createArrayFromRest( + referencedData[name], [references[name].targetField()], - referencedData[name] + references[name].targetEntity().name(), + references[name].targetEntity().identifier().name() ); dataStore.setEntries( @@ -173,6 +182,7 @@ function routing($stateProvider) { }, resolve: { dataStore: dataStoreProvider(), + Entry: entryConstructorProvider(), view: viewProvider('ShowView'), rawEntry: ['$stateParams', 'ReadQueries', 'view', function ($stateParams, ReadQueries, view) { return ReadQueries.getOne(view.getEntity(), view.type, $stateParams.id, view.identifier(), view.getUrl()); @@ -186,17 +196,17 @@ function routing($stateProvider) { optimizedReferencedData: ['ReadQueries', 'view', 'entry', function (ReadQueries, view, entry) { return ReadQueries.getOptimizedReferencedData(view.getOptimizedReferences(), [entry.values]); }], - referencedEntries: ['dataStore', 'view', 'nonOptimizedReferencedData', 'optimizedReferencedData', function (dataStore, view, nonOptimizedReferencedData, optimizedReferencedData) { + referencedEntries: ['dataStore', 'Entry', 'view', 'nonOptimizedReferencedData', 'optimizedReferencedData', function (dataStore, Entry, view, nonOptimizedReferencedData, optimizedReferencedData) { var references = view.getReferences(), referencedData = angular.extend(nonOptimizedReferencedData, optimizedReferencedData), referencedEntries; for (var name in referencedData) { - referencedEntries = dataStore.mapEntries( - references[name].targetEntity().name(), - references[name].targetEntity().identifier(), + referencedEntries = Entry.createArrayFromRest( + referencedData[name], [references[name].targetField()], - referencedData[name] + references[name].targetEntity().name(), + references[name].targetEntity().identifier() ); dataStore.setEntries( @@ -214,7 +224,7 @@ function routing($stateProvider) { return ReadQueries.getReferencedListData(referencedLists, sortField, sortDir, entry.identifierValue); }], - referencedListEntries: ['dataStore', 'view', 'referencedListData', function (dataStore, view, referencedListData) { + referencedListEntries: ['dataStore', 'Entry', 'view', 'referencedListData', function (dataStore, Entry, view, referencedListData) { var referencedLists = view.getReferencedLists(); var referencedList; var referencedListEntries; @@ -223,11 +233,11 @@ function routing($stateProvider) { referencedList = referencedLists[i]; referencedListEntries = referencedListData[i]; - referencedListEntries = dataStore.mapEntries( - referencedList.targetEntity().name(), - referencedList.targetEntity().identifier(), + referencedListEntries = Entry.createArrayFromRest( + referencedListEntries, referencedList.targetFields(), - referencedListEntries + referencedList.targetEntity().name(), + referencedList.targetEntity().identifier() ); dataStore.setEntries( @@ -261,6 +271,7 @@ function routing($stateProvider) { resolve: { dataStore: dataStoreProvider(), view: viewProvider('CreateView'), + Entry: entryConstructorProvider(), entry: ['dataStore', 'view', function (dataStore, view) { var entry = dataStore.createEntry(view.entity.name(), view.identifier(), view.getFields()); dataStore.addEntry(view.getEntity().uniqueId, entry); @@ -270,16 +281,16 @@ function routing($stateProvider) { choiceData: ['ReadQueries', 'view', function (ReadQueries, view) { return ReadQueries.getAllReferencedData(view.getReferences(false)); }], - choiceEntries: ['dataStore', 'view', 'choiceData', function (dataStore, view, filterData) { + choiceEntries: ['dataStore', 'Entry', 'view', 'choiceData', function (dataStore, Entry, view, filterData) { var choices = view.getReferences(false); var choiceEntries; for (var name in filterData) { - choiceEntries = dataStore.mapEntries( - choices[name].targetEntity().name(), - choices[name].targetEntity().identifier(), + choiceEntries = Entry.createArrayFromRest( + filterData[name], [choices[name].targetField()], - filterData[name] + choices[name].targetEntity().name(), + choices[name].targetEntity().identifier() ); dataStore.setEntries( @@ -310,6 +321,7 @@ function routing($stateProvider) { }, resolve: { dataStore: dataStoreProvider(), + Entry: entryConstructorProvider(), view: viewProvider('EditView'), rawEntry: ['$stateParams', 'ReadQueries', 'view', function ($stateParams, ReadQueries, view) { return ReadQueries.getOne(view.getEntity(), view.type, $stateParams.id, view.identifier(), view.getUrl()); @@ -323,17 +335,17 @@ function routing($stateProvider) { optimizedReferencedData: ['ReadQueries', 'view', 'entry', function (ReadQueries, view, entry) { return ReadQueries.getOptimizedReferencedData(view.getOptimizedReferences(), [entry.values]); }], - referencedEntries: ['dataStore', 'view', 'nonOptimizedReferencedData', 'optimizedReferencedData', function (dataStore, view, nonOptimizedReferencedData, optimizedReferencedData) { + referencedEntries: ['dataStore', 'Entry', 'view', 'nonOptimizedReferencedData', 'optimizedReferencedData', function (dataStore, Entry, view, nonOptimizedReferencedData, optimizedReferencedData) { var references = view.getReferences(), referencedData = angular.extend(nonOptimizedReferencedData, optimizedReferencedData), referencedEntries; for (var name in referencedData) { - referencedEntries = dataStore.mapEntries( - references[name].targetEntity().name(), - references[name].targetEntity().identifier(), + referencedEntries = Entry.createArrayFromRest( + referencedData[name], [references[name].targetField()], - referencedData[name] + references[name].targetEntity().name(), + references[name].targetEntity().identifier() ); dataStore.setEntries( @@ -351,7 +363,7 @@ function routing($stateProvider) { return ReadQueries.getReferencedListData(referencedLists, sortField, sortDir, entry.identifierValue); }], - referencedListEntries: ['dataStore', 'view', 'referencedListData', function (dataStore, view, referencedListData) { + referencedListEntries: ['dataStore', 'Entry', 'view', 'referencedListData', function (dataStore, Entry, view, referencedListData) { var referencedLists = view.getReferencedLists(); var referencedList; var referencedListEntries; @@ -360,11 +372,11 @@ function routing($stateProvider) { referencedList = referencedLists[i]; referencedListEntries = referencedListData[i]; - referencedListEntries = dataStore.mapEntries( - referencedList.targetEntity().name(), - referencedList.targetEntity().identifier(), + referencedListEntries = Entry.createArrayFromRest( + referencedListEntries, referencedList.targetFields(), - referencedListEntries + referencedList.targetEntity().name(), + referencedList.targetEntity().identifier() ); dataStore.setEntries( @@ -382,16 +394,16 @@ function routing($stateProvider) { choiceData: ['ReadQueries', 'view', function (ReadQueries, view) { return ReadQueries.getAllReferencedData(view.getReferences(false)); }], - choiceEntries: ['dataStore', 'view', 'choiceData', function (dataStore, view, filterData) { + choiceEntries: ['dataStore', 'Entry', 'view', 'choiceData', function (dataStore, Entry, view, filterData) { var choices = view.getReferences(false); var choiceEntries; for (var name in filterData) { - choiceEntries = dataStore.mapEntries( - choices[name].targetEntity().name(), - choices[name].targetEntity().identifier(), + choiceEntries = Entry.createArrayFromRest( + filterData[name], [choices[name].targetField()], - filterData[name] + choices[name].targetEntity().name(), + choices[name].targetEntity().identifier() ); dataStore.setEntries( From cb6786d0bd33b20aca6aa3ea24a33c72e86fd059 Mon Sep 17 00:00:00 2001 From: Francois Zaninotto Date: Wed, 8 Jul 2015 18:20:59 +0200 Subject: [PATCH 05/19] Update admin-config dependency to updated package --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a20f5bdc..df1a2d3f 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "npm": "^2.10.0" }, "devDependencies": { - "admin-config": "^0.2.4", + "admin-config": "marmelab/admin-config#map_transform", "angular": "~1.3.15", "angular-bootstrap": "^0.12.0", "angular-mocks": "1.3.14", From bd13dc65b441c94976eea239902074e2fc38cc10 Mon Sep 17 00:00:00 2001 From: Francois Zaninotto Date: Wed, 8 Jul 2015 23:13:56 +0200 Subject: [PATCH 06/19] Handle creation of nested properties --- .../ng-admin/Crud/form/FormController.js | 81 ++++++------------- src/javascripts/ng-admin/Crud/routing.js | 4 +- 2 files changed, 28 insertions(+), 57 deletions(-) diff --git a/src/javascripts/ng-admin/Crud/form/FormController.js b/src/javascripts/ng-admin/Crud/form/FormController.js index d3140b43..0e335dbe 100644 --- a/src/javascripts/ng-admin/Crud/form/FormController.js +++ b/src/javascripts/ng-admin/Crud/form/FormController.js @@ -25,83 +25,54 @@ var FormController = function ($scope, $state, WriteQueries, Configuration, }; FormController.prototype.validateEntry = function () { - var value, - form = this.form, - entry = this.$scope.entry, - fields = this.view.getFields(), - identifierField = this.entity.identifier(), - mappedObject, - field, - i, - object = {}; - - if (!form.$valid) { - this.notification.log('invalid form', {addnCls: 'humane-flatty-error'}); + if (!this.form.$valid) { + this.notification.log('invalid form', { addnCls: 'humane-flatty-error' }); return false; } - // Inject identifier - object[identifierField.name()] = entry.identifierValue; - - for (i in fields) { - field = fields[i]; - value = entry.values[field.name()]; - object[field.name()] = value; - } - - mappedObject = this.dataStore.mapEntry( - this.view.entity.name(), - this.view.identifier(), - this.view.getFields(), - object - ); - try { - this.view.validate(mappedObject); + this.view.validate(this.$scope.entry); } catch (e) { - this.notification.log(e, {addnCls: 'humane-flatty-error'}); + this.notification.log(e, { addnCls: 'humane-flatty-error' }); return false; } - return object; + return true; }; FormController.prototype.submitCreation = function ($event) { $event.preventDefault(); - var entry = this.validateEntry(); - var entity = this.entity; - var route = !entity.editionView().enabled ? 'show' : 'edit'; - if (!entry) { + if (!this.validateEntry()) { return; } - var progression = this.progression, - notification = this.notification, - $state = this.$state; - progression.start(); + var entity = this.entity; + var view = this.view; + var route = !entity.editionView().enabled ? 'show' : 'edit'; + var restEntry = this.$scope.entry.transformToRest(view.fields()); + this.progression.start(); this.WriteQueries - .createOne(this.view, entry) - .then(function (rawEntry) { - var entry = this.dataStore.mapEntry(entity.name(), this.view.identifier(), this.view.getFields(), rawEntry); - progression.done(); - notification.log('Element successfully created.', {addnCls: 'humane-flatty-success'}); - $state.go($state.get(route), { entity: entity.name(), id: entry.identifierValue }); - }.bind(this), this.handleError.bind(this)); + .createOne(view, restEntry) + .then(rawEntry => { + this.progression.done(); + this.notification.log('Element successfully created.', { addnCls: 'humane-flatty-success' }); + var entry = view.mapEntry(rawEntry); + this.$state.go(this.$state.get(route), { entity: entity.name(), id: entry.identifierValue }); + }, this.handleError.bind(this)); }; FormController.prototype.submitEdition = function ($event) { $event.preventDefault(); - var entry = this.validateEntry(); - if (!entry) { + if (!this.validateEntry()) { return; } - var progression = this.progression, - notification = this.notification; - progression.start(); + var view = this.view; + var restEntry = this.$scope.entry.transformToRest(view.fields()); + this.progression.start(); this.WriteQueries - .updateOne(this.view, entry, this.originEntityId) - .then(function () { - progression.done(); - notification.log('Changes successfully saved.', {addnCls: 'humane-flatty-success'}); + .updateOne(view, restEntry, this.originEntityId) + .then(() => { + this.progression.done(); + this.notification.log('Changes successfully saved.', { addnCls: 'humane-flatty-success' }); }, this.handleError.bind(this)); }; diff --git a/src/javascripts/ng-admin/Crud/routing.js b/src/javascripts/ng-admin/Crud/routing.js index 9c590fac..90ecaed2 100644 --- a/src/javascripts/ng-admin/Crud/routing.js +++ b/src/javascripts/ng-admin/Crud/routing.js @@ -272,8 +272,8 @@ function routing($stateProvider) { dataStore: dataStoreProvider(), view: viewProvider('CreateView'), Entry: entryConstructorProvider(), - entry: ['dataStore', 'view', function (dataStore, view) { - var entry = dataStore.createEntry(view.entity.name(), view.identifier(), view.getFields()); + entry: ['dataStore', 'Entry', 'view', function (dataStore, Entry, view) { + var entry = Entry.createForFields(view.getFields(), view.entity.name()); dataStore.addEntry(view.getEntity().uniqueId, entry); return entry; From 8ade32e23cb28f35881111ef04b5ce174564cb4d Mon Sep 17 00:00:00 2001 From: Francois Zaninotto Date: Wed, 8 Jul 2015 23:35:10 +0200 Subject: [PATCH 07/19] Fix wrong calls to reference populator --- src/javascripts/ng-admin/Crud/routing.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/javascripts/ng-admin/Crud/routing.js b/src/javascripts/ng-admin/Crud/routing.js index 90ecaed2..3943ddd6 100644 --- a/src/javascripts/ng-admin/Crud/routing.js +++ b/src/javascripts/ng-admin/Crud/routing.js @@ -206,7 +206,7 @@ function routing($stateProvider) { referencedData[name], [references[name].targetField()], references[name].targetEntity().name(), - references[name].targetEntity().identifier() + references[name].targetEntity().identifier().name() ); dataStore.setEntries( @@ -237,7 +237,7 @@ function routing($stateProvider) { referencedListEntries, referencedList.targetFields(), referencedList.targetEntity().name(), - referencedList.targetEntity().identifier() + referencedList.targetEntity().identifier().name() ); dataStore.setEntries( @@ -290,7 +290,7 @@ function routing($stateProvider) { filterData[name], [choices[name].targetField()], choices[name].targetEntity().name(), - choices[name].targetEntity().identifier() + choices[name].targetEntity().identifier().name() ); dataStore.setEntries( @@ -345,7 +345,7 @@ function routing($stateProvider) { referencedData[name], [references[name].targetField()], references[name].targetEntity().name(), - references[name].targetEntity().identifier() + references[name].targetEntity().identifier().name() ); dataStore.setEntries( @@ -376,7 +376,7 @@ function routing($stateProvider) { referencedListEntries, referencedList.targetFields(), referencedList.targetEntity().name(), - referencedList.targetEntity().identifier() + referencedList.targetEntity().identifier().name() ); dataStore.setEntries( @@ -403,7 +403,7 @@ function routing($stateProvider) { filterData[name], [choices[name].targetField()], choices[name].targetEntity().name(), - choices[name].targetEntity().identifier() + choices[name].targetEntity().identifier().name() ); dataStore.setEntries( From d4f6c420244b56c52f637f12d842e29e2f016939 Mon Sep 17 00:00:00 2001 From: Francois Zaninotto Date: Wed, 8 Jul 2015 23:35:34 +0200 Subject: [PATCH 08/19] Use new RestMapper in export to CSV button --- .../Crud/button/maExportToCsvButton.js | 37 +++++++------------ 1 file changed, 14 insertions(+), 23 deletions(-) diff --git a/src/javascripts/ng-admin/Crud/button/maExportToCsvButton.js b/src/javascripts/ng-admin/Crud/button/maExportToCsvButton.js index d4bd62f5..287ac53c 100644 --- a/src/javascripts/ng-admin/Crud/button/maExportToCsvButton.js +++ b/src/javascripts/ng-admin/Crud/button/maExportToCsvButton.js @@ -3,7 +3,7 @@ define(function () { 'use strict'; - function maExportToCsvButton ($stateParams, Papa, notification, entryFormatter, ReadQueries) { + function maExportToCsvButton ($stateParams, Papa, notification, AdminDescription, entryFormatter, ReadQueries) { return { restrict: 'E', scope: { @@ -37,34 +37,28 @@ define(function () { var optimizedReferencedData; ReadQueries.getAll(exportView, -1, scope.search(), $stateParams.sortField, $stateParams.sortDir) - .then(function (response) { + .then(response => { rawEntries = response.data; - return rawEntries; - }, function (error) { - notification.log(error.message, {addnCls: 'humane-flatty-error'}); }) - .then(function (rawEntries) { - return ReadQueries.getFilteredReferenceData(exportView.getNonOptimizedReferences(), rawEntries); - }) - .then(function (nonOptimizedReference) { + .then(rawEntries => ReadQueries.getFilteredReferenceData(exportView.getNonOptimizedReferences(), rawEntries)) + .then(nonOptimizedReference => { nonOptimizedReferencedData = nonOptimizedReference; - return ReadQueries.getOptimizedReferencedData(exportView.getOptimizedReferences(), rawEntries); }) - .then(function (optimizedReference) { + .then(optimizedReference => { optimizedReferencedData = optimizedReference; - var references = exportView.getReferences(), referencedData = angular.extend(nonOptimizedReferencedData, optimizedReferencedData), referencedEntries; for (var name in referencedData) { - referencedEntries = scope.datastore.mapEntries( - references[name].targetEntity().name(), - references[name].targetEntity().identifier(), + referencedEntries = AdminDescription.getEntryConstructor().createArrayFromRest( + referencedData[name], [references[name].targetField()], - referencedData[name] + references[name].targetEntity().name(), + references[name].targetEntity().identifier().name() + ); scope.datastore.setEntries( @@ -74,12 +68,7 @@ define(function () { } }) .then(function () { - var entries = scope.datastore.mapEntries( - exportView.entity.name(), - exportView.identifier(), - exportView.getFields(), - rawEntries - ); + var entries = exportView.mapEntries(rawEntries); // shortcut to diplay collection of entry with included referenced values scope.datastore.fillReferencesValuesFromCollection(entries, exportView.getReferences(), true); @@ -95,13 +84,15 @@ define(function () { fakeLink.setAttribute('href', 'data:application/octet-stream;charset=utf-8,' + encodeURIComponent(csv)); fakeLink.setAttribute('download', scope.entity.name() + '.csv'); fakeLink.click(); + }, function (error) { + notification.log(error.message, {addnCls: 'humane-flatty-error'}); }); }; } }; } - maExportToCsvButton.$inject = ['$stateParams', 'Papa', 'notification', 'EntryFormatter', 'ReadQueries']; + maExportToCsvButton.$inject = ['$stateParams', 'Papa', 'notification', 'AdminDescription', 'EntryFormatter', 'ReadQueries']; return maExportToCsvButton; }); From 8d7f7338ffe77d0d1c026ac53c268f630fe87cf8 Mon Sep 17 00:00:00 2001 From: Francois Zaninotto Date: Wed, 8 Jul 2015 23:47:12 +0200 Subject: [PATCH 09/19] Remove call from dataStore.mapEntries in infinite pagination --- .../ng-admin/Crud/list/ListController.js | 30 ++++++++----------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/src/javascripts/ng-admin/Crud/list/ListController.js b/src/javascripts/ng-admin/Crud/list/ListController.js index 268ddf33..51aff4fe 100644 --- a/src/javascripts/ng-admin/Crud/list/ListController.js +++ b/src/javascripts/ng-admin/Crud/list/ListController.js @@ -41,28 +41,24 @@ define(function () { return; } - var progression = this.progression, - self = this; + let view = this.view, + datastore = this.datastore; - progression.start(); + this.progression.start(); this.ReadQueries - .getAll(this.view, page, this.search, this.sortField, this.sortDir) - .then(function (response) { - progression.done(); - var references = self.view.getReferences(); + .getAll(view, page, this.search, this.sortField, this.sortDir) + .then(response => { + this.progression.done(); + var references = view.getReferences(); - self.dataStore.mapEntries( - self.entity.name(), - self.view.identifier(), - self.fields, - response.data - ).map(function (entry) { - self.dataStore.fillReferencesValuesFromEntry(entry, references, true); - self.dataStore.addEntry(self.entity.uniqueId, entry); - }); + view.mapEntries(response.data) + .map(entry => { + dataStore.fillReferencesValuesFromEntry(entry, references, true); + dataStore.addEntry(this.entity.uniqueId, entry); + }); - self.loadingPage = false; + this.loadingPage = false; }); }; From ad0241c3aeb04a6ae17926187d2840bcd637e033 Mon Sep 17 00:00:00 2001 From: Francois Zaninotto Date: Wed, 8 Jul 2015 23:52:36 +0200 Subject: [PATCH 10/19] Remove call from datastore.mapEntries() in the dashboard --- .../ng-admin/Main/config/routing.js | 43 +++++++++---------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/src/javascripts/ng-admin/Main/config/routing.js b/src/javascripts/ng-admin/Main/config/routing.js index a672090b..76aeed8c 100644 --- a/src/javascripts/ng-admin/Main/config/routing.js +++ b/src/javascripts/ng-admin/Main/config/routing.js @@ -8,6 +8,12 @@ function dataStoreProvider() { }]; } +function entryConstructorProvider() { + return ['AdminDescription', function (AdminDescription) { + return AdminDescription.getEntryConstructor(); + }]; +} + function routing($stateProvider, $urlRouterProvider) { $stateProvider.state('main', { @@ -33,13 +39,14 @@ function routing($stateProvider, $urlRouterProvider) { }], resolve: { dataStore: dataStoreProvider(), + Entry: entryConstructorProvider(), hasEntities: ['NgAdminConfiguration', function(Configuration) { return Configuration().entities.length > 0; }], collections: ['NgAdminConfiguration', function(Configuration) { return Configuration().dashboard().collections(); }], - responses: ['$stateParams', '$q', 'collections', 'dataStore', 'ReadQueries', function($stateParams, $q, collections, dataStore, ReadQueries) { + responses: ['$stateParams', '$q', 'collections', 'dataStore', 'Entry', 'ReadQueries', function($stateParams, $q, collections, dataStore, Entry, ReadQueries) { var sortField = 'sortField' in $stateParams ? $stateParams.sortField : null; var sortDir = 'sortDir' in $stateParams ? $stateParams.sortDir : null; @@ -62,20 +69,17 @@ function routing($stateProvider, $urlRouterProvider) { return ReadQueries .getAll(collection, 1, {}, collectionSortField, collectionSortDir) - .then(function (response) { + .then(response => { rawEntries = response.data; - return rawEntries; }) - .then(function (rawEntries) { - return ReadQueries.getFilteredReferenceData(collection.getNonOptimizedReferences(), rawEntries); - }) - .then(function (nonOptimizedReference) { + .then(rawEntries => ReadQueries.getFilteredReferenceData(collection.getNonOptimizedReferences(), rawEntries) + ) + .then(nonOptimizedReference => { nonOptimizedReferencedData = nonOptimizedReference; - return ReadQueries.getOptimizedReferencedData(collection.getOptimizedReferences(), rawEntries); }) - .then(function (optimizedReference) { + .then(optimizedReference => { optimizedReferencedData = optimizedReference; var references = collection.getReferences(), @@ -83,11 +87,11 @@ function routing($stateProvider, $urlRouterProvider) { referencedEntries; for (var name in referencedData) { - referencedEntries = dataStore.mapEntries( - references[name].targetEntity().name(), - references[name].targetEntity().identifier(), + referencedEntries = Entry.createArrayFromRest( + referencedData[name], [references[name].targetField()], - referencedData[name] + references[name].targetEntity().name(), + references[name].targetEntity().identifier().name() ); dataStore.setEntries( @@ -96,15 +100,10 @@ function routing($stateProvider, $urlRouterProvider) { ); } }) - .then(function () { - var entries = dataStore.mapEntries( - collection.entity.name(), - collection.identifier(), - collection.getFields(), - rawEntries - ); - - // shortcut to diplay collection of entry with included referenced values + .then(() => { + var entries = collection.mapEntries(rawEntries); + + // shortcut to display collection of entry with included referenced values dataStore.fillReferencesValuesFromCollection(entries, collection.getReferences(), true); return entries; From 7fea3624dd1b74574d62a4037687cd73d237fe82 Mon Sep 17 00:00:00 2001 From: Francois Zaninotto Date: Wed, 8 Jul 2015 23:56:27 +0200 Subject: [PATCH 11/19] Update ListViewSpec to use new syntax --- .../unit/Main/component/service/config/view/ListViewSpec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/javascripts/test/unit/Main/component/service/config/view/ListViewSpec.js b/src/javascripts/test/unit/Main/component/service/config/view/ListViewSpec.js index b986df70..ac05d6cc 100644 --- a/src/javascripts/test/unit/Main/component/service/config/view/ListViewSpec.js +++ b/src/javascripts/test/unit/Main/component/service/config/view/ListViewSpec.js @@ -28,7 +28,7 @@ describe("Service: ListView config", function () { return value.substr(0, 5) + '...'; })); - var entries = dataStore.mapEntries(list.entity.name(), list.identifier(), list.getFields(), [ + var entries = list.mapEntries([ { id: 1, human_id: 1, name: 'Suna'}, { id: 2, human_id: 2, name: 'Boby'}, { id: 3, human_id: 1, name: 'Mizute'} From d3f2d4dc6caa19b2bacaa0301aae68fcd5b4b876 Mon Sep 17 00:00:00 2001 From: Francois Zaninotto Date: Thu, 9 Jul 2015 00:34:52 +0200 Subject: [PATCH 12/19] Add e2e test for nested inputs display in edit view --- .../ng-admin/Crud/field/maField.js | 2 +- src/javascripts/test/e2e/EditionViewSpec.js | 41 ++++++++++++++----- 2 files changed, 32 insertions(+), 11 deletions(-) diff --git a/src/javascripts/ng-admin/Crud/field/maField.js b/src/javascripts/ng-admin/Crud/field/maField.js index c9e5f79d..c2d4bea0 100644 --- a/src/javascripts/ng-admin/Crud/field/maField.js +++ b/src/javascripts/ng-admin/Crud/field/maField.js @@ -42,7 +42,7 @@ define(function (require) { scope.datastore = scope.datastore(); scope.getClassesForField = function(field, entry) { - return 'ng-admin-field-' + field.name() + ' ' + (field.getCssClasses(entry) || 'col-sm-10 col-md-8 col-lg-7'); + return 'ng-admin-field-' + field.name().replace('.', '_') + ' ' + (field.getCssClasses(entry) || 'col-sm-10 col-md-8 col-lg-7'); }; scope.getInputForField = function(field) { diff --git a/src/javascripts/test/e2e/EditionViewSpec.js b/src/javascripts/test/e2e/EditionViewSpec.js index e6eea979..688f36c0 100644 --- a/src/javascripts/test/e2e/EditionViewSpec.js +++ b/src/javascripts/test/e2e/EditionViewSpec.js @@ -1,19 +1,40 @@ /*global describe,it,expect,$$,element,browser,by*/ -describe('EditionViews', function () { +describe('EditionView', function () { 'use strict'; - var hasToLoad = true; - beforeEach(function() { - if (hasToLoad) { - browser.get(browser.baseUrl + '#/posts/edit/1'); - hasToLoad = false; - } - }); + describe('Fields mapping', function() { + + beforeEach(function() { + browser.get(browser.baseUrl + '#/comments/edit/11'); + }); + + it('should map nested fields from the REST response', function () { + $$('.ng-admin-field-author_name input').then(function (inputs) { + expect(inputs.length).toBe(1); + expect(inputs[0].getAttribute('value')).toBe('Logan Schowalter'); + }); + }); + }) describe('ChoiceField', function() { + + beforeEach(function() { + browser.get(browser.baseUrl + '#/posts/edit/1'); + }); + it('should render correctly choice fields', function () { - $$('.ng-admin-field-category .ui-select-container').then(function (uiSelect) { - expect(uiSelect.length).toBe(1); + $$('.ng-admin-field-category .ui-select-container') + .then(function(uiSelect) { + expect(uiSelect.length).toBe(1) + }) + .then(function() { + return $$('.ng-admin-field-category .btn').first().click(); + }) + .then(function() { + return $$('.ng-admin-field-category .ui-select-choices-row'); + }) + .then(function(choices) { + expect(choices.length).toBe(2) }); }); }); From 01fb978fba7c895fec94419c84ea79aa949caaf1 Mon Sep 17 00:00:00 2001 From: Francois Zaninotto Date: Thu, 9 Jul 2015 08:52:53 +0200 Subject: [PATCH 13/19] mention the change in map() behavior --- UPGRADE-0.8.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/UPGRADE-0.8.md b/UPGRADE-0.8.md index 658ae2bd..a941bb4c 100644 --- a/UPGRADE-0.8.md +++ b/UPGRADE-0.8.md @@ -73,3 +73,26 @@ admin.dashboard(nga.dashboard() See the [Dashboard Configuration](doc/Dashboard.md) dedicated chapter for more details. Calls to `dashboardView()` are still supported in ng-admin 0.8, but will raise an error in future versions. + +## `field.map()` callbacks no longer apply to POST and PUT queries + +Registering a transformation callback using `field.map()` used to be applied both ways: from the REST response to the Entry object used by ng-admin (in read queries using GET), and from the Entry object to the body of the REST requests (for write queries using POST and PUT). This didn't make sense, and prevented a proper use of this `map()` feature in the edition and creation view. + +Now `map()` only applies to read queries, and a symmetric feature called `transform()` was added to handle the transformation of ng-admin values from forms to REST requests. + +```js +// API +// map() v ^ tranform() +// Entry︎ +// +// The API provides and expects last names in all caps, e.g. 'DOE' +// The admin should display them with capitalized last names, e.g 'Doe' +nga.field('last_name') + .map(function capitalize(value, entry) { + return value.substr(0,1).toUpperCase() + value.substr(1).toLowerCase() + }) + .transform(function allCaps(value, entry) { + // the API expects upper case last names + return value.toUpperCase(); + }); +``` From 987d7047c83aa1c7d6fafb48b6dfd036dbbf8d7a Mon Sep 17 00:00:00 2001 From: Francois Zaninotto Date: Thu, 9 Jul 2015 09:59:22 +0200 Subject: [PATCH 14/19] bump json-server to get q filtering on nested vales --- examples/blog/config.js | 2 ++ package.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/blog/config.js b/examples/blog/config.js index f3dba621..2aa6297e 100644 --- a/examples/blog/config.js +++ b/examples/blog/config.js @@ -28,7 +28,9 @@ // custom sort params if (params._sortField) { params._sort = params._sortField; + params._order = params._sortDir; delete params._sortField; + delete params._sortDir; } // custom filters if (params._filters) { diff --git a/package.json b/package.json index df1a2d3f..730caa6e 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,7 @@ "grunt-contrib-uglify": "~0.6.0", "grunt-contrib-watch": "~0.5.2", "grunt-exec": "^0.4.6", - "grunt-json-server": "git://github.com/fzaninotto/grunt-json-server.git#c834e50e2151a16930f1378324cf96b91438bdae", + "grunt-json-server": "git://github.com/fzaninotto/grunt-json-server.git", "grunt-karma": "^0.8.3", "grunt-mocha-test": "^0.12.7", "grunt-ng-annotate": "^0.10.0", From 5c6825c07f9c802ace665214f8596a780ec8196b Mon Sep 17 00:00:00 2001 From: Francois Zaninotto Date: Thu, 9 Jul 2015 10:18:55 +0200 Subject: [PATCH 15/19] Disable grunt server logging --- Gruntfile.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Gruntfile.js b/Gruntfile.js index afd95829..731c1664 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -43,7 +43,8 @@ module.exports = function (grunt) { options: { port: 3000, db: 'examples/blog/stub-server.json', - keepalive: false + keepalive: false, + logger: false } } }, From 7e3f9e1d487794aea3c5eed9d712ac67b30464a3 Mon Sep 17 00:00:00 2001 From: Francois Zaninotto Date: Thu, 9 Jul 2015 10:47:06 +0200 Subject: [PATCH 16/19] Add doc about the new deeply nested capabilities --- doc/API-mapping.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/doc/API-mapping.md b/doc/API-mapping.md index 3db44b79..c926cf42 100644 --- a/doc/API-mapping.md +++ b/doc/API-mapping.md @@ -76,6 +76,27 @@ app.config(function(RestangularProvider) { } ``` +**Tip**: If you want to define a field mapped to a deeply nested property, you don't need an interceptor. Just define the field with a name made by the path to the property using dots as separators: + +```json +{ + "id": 12, + "name": "War and Peace", + "author": { + "id": 345, + "name": "Leo Tolstoi" + }, + "publication_date": "2014-01-01T00:00:00Z" +} +``` + +```js +listView.fields([ + nga.field('name'), + nga.field('author.name').label('Author name') +]) +``` + ## Pagination ng-admin assumes that your API accepts `_page` and `_perPage` query parameters to paginate lists: From 7461a649b2356473232cd274d7fa0060a4eb095d Mon Sep 17 00:00:00 2001 From: Francois Zaninotto Date: Thu, 9 Jul 2015 10:47:43 +0200 Subject: [PATCH 17/19] Improve Edition test to actually save a modification --- src/javascripts/test/e2e/EditionViewSpec.js | 30 ++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/src/javascripts/test/e2e/EditionViewSpec.js b/src/javascripts/test/e2e/EditionViewSpec.js index 688f36c0..c6d81b5a 100644 --- a/src/javascripts/test/e2e/EditionViewSpec.js +++ b/src/javascripts/test/e2e/EditionViewSpec.js @@ -9,9 +9,37 @@ describe('EditionView', function () { }); it('should map nested fields from the REST response', function () { - $$('.ng-admin-field-author_name input').then(function (inputs) { + $$('.ng-admin-field-author_name input') + .then(function (inputs) { expect(inputs.length).toBe(1); expect(inputs[0].getAttribute('value')).toBe('Logan Schowalter'); + return inputs[0]; + }) + .then(function(input) { + return input.sendKeys('r'); + }) + .then(function() { + return $$('button[type="submit"]').first().click(); + }) + .then(function() { + return browser.get(browser.baseUrl + '#/comments/list'); + }) + .then(function() { + return browser.get(browser.baseUrl + '#/comments/edit/11'); + }) + .then(function() { + return $$('.ng-admin-field-author_name input').first(); + }) + .then(function (input) { + expect(input.getAttribute('value')).toBe('Logan Schowalterr'); + // the data was modified in the server, we need to change it back to its original value + return input; + }) + .then(function(input) { + return input.sendKeys("\b"); + }) + .then(function() { + return $$('button[type="submit"]').first().click(); }); }); }) From d301650d1fbf6709785fd957528c91305adb5cdc Mon Sep 17 00:00:00 2001 From: Francois Zaninotto Date: Thu, 9 Jul 2015 11:01:14 +0200 Subject: [PATCH 18/19] No need to define the label anymore --- doc/API-mapping.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/API-mapping.md b/doc/API-mapping.md index c926cf42..bd58dae0 100644 --- a/doc/API-mapping.md +++ b/doc/API-mapping.md @@ -93,7 +93,7 @@ app.config(function(RestangularProvider) { ```js listView.fields([ nga.field('name'), - nga.field('author.name').label('Author name') + nga.field('author.name') ]) ``` From e1dacb1a0bf6d41d002981b4d6ca1b5c53166db6 Mon Sep 17 00:00:00 2001 From: Francois Zaninotto Date: Thu, 9 Jul 2015 14:21:39 +0200 Subject: [PATCH 19/19] Bump to new fixed admin-config version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 730caa6e..54372e2a 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "npm": "^2.10.0" }, "devDependencies": { - "admin-config": "marmelab/admin-config#map_transform", + "admin-config": "^0.2.5", "angular": "~1.3.15", "angular-bootstrap": "^0.12.0", "angular-mocks": "1.3.14",