diff --git a/src/main/javascript/controller/cat-base-edit-controller.js b/src/main/javascript/controller/cat-base-edit-controller.js new file mode 100644 index 0000000..30d941c --- /dev/null +++ b/src/main/javascript/controller/cat-base-edit-controller.js @@ -0,0 +1,147 @@ +'use strict'; + +angular.module('cat.controller.base.edit', []) + +/** + * @ngdoc controller + * @name cat.controller.base.edit:CatBaseEditController + * + * @description + * The CatBaseEditController takes care of providing several common properties and functions to the scope + * of every edit page. It also instantiates the controller given via the config.controller parameter and shares + * the same scope with it. + * + * Common properties include: + * * detail - the actual object to view + * * editDetail - a copy of the detail object used for editing + * * breadcrumbs - the breadcrumbs array + * * uiStack - the ui stack array if parents exist + * * editTemplate - the url of the edit template + * * mainViewTemplate - the url of the main view template + * * additionalViewTemplate - the url of the additional view template if it exists + * * $fieldErrors - a map of validation errors returned by the server + * + * Common functions include: + * * save - the save function to update / create an object + * * cancelEdit - a function to switch from edit to view mode (discarding all changes) + * * title - a function to resolve a 'title' of the current object + * + * @param {object} $scope DOCTODO + * @param {object} $state DOCTODO + * @param {object} $stateParams DOCTODO + * @param {object} $window DOCTODO + * @param {object} $globalMessages DOCTODO + * @param {object} $controller DOCTODO + * @param {object} $log DOCTODO + * @param {object} catBreadcrumbsService DOCTODO + * @param {Object} config holds data like the current api endpoint, template urls, base url, the model constructor, etc. + * @param {object} catValidationService DOCTODO + * @constructor + */ + .controller('CatBaseEditController', CatBaseEditController); + +function CatBaseEditController($scope, $state, $stateParams, $window, $globalMessages, $controller, $log, catBreadcrumbsService, catValidationService, config) { + $scope.detail = config.detail; + $scope.editDetail = undefined; + $scope.$fieldErrors = {}; + + $scope.config = config; + var endpoint = config.endpoint; + var baseUrl = config.baseUrl; + var templateUrls = config.templateUrls; + var Model = config.Model; + + $scope.uiStack = catBreadcrumbsService.generateFromConfig(config); + + if ($stateParams.id === 'new' || $stateParams.id === undefined) { + catBreadcrumbsService.push({ + title: 'New', + key: 'cc.catalysts.general.new' + }); + } else { + catBreadcrumbsService.push({}); + } + + $scope.editTemplate = templateUrls.edit; + + if (_.isObject(templateUrls.view)) { + $scope.mainViewTemplate = templateUrls.view.main; + $scope.additionalViewTemplate = templateUrls.view.additional; + } else { + $scope.mainViewTemplate = templateUrls.view; + } + + $scope.baseUrl = baseUrl; + + /** + * @returns {String|Number} A title of the current object or the 'id' as fallback + */ + $scope.title = function () { + var data = $scope.detail; + if (_.isUndefined(data)) { + return ''; + } + return !!data.breadcrumbTitle ? data.breadcrumbTitle() : (!!data.name ? data.name : data.id); + }; + + /** + * reloads the current object from the server + */ + $scope.reloadDetails = function () { + endpoint.get($stateParams.id).then(function (data) { + $scope.detail = data; + update(); + }); + }; + + $scope.exists = !!$stateParams.id && $stateParams.id !== 'new'; + + $scope.cancelEdit = function () { + $scope.$broadcast('formReset'); // TODO necessary to call? + if ($scope.exists) { + $state.go(config.name + '.detail', {id: config.detail.id}); + } else { + $window.history.back(); + } + }; + + /** + * Calls the save function of the current endpoint. + * Upon success the view mode of the details of the currently created / updated object will be shown. + * Upon an error the reported errors (global & field errors) will be shown to the user and the edit mode + * will remain active. + * + * * @param {object} stayInEdit If true the view stays in detail edit state after save instead of switching to + * detail view state. + */ + $scope.save = function (stayInEdit) { + endpoint.save($scope.editDetail).then(function (data) { + catValidationService.clearValidationErrors(); + $scope.$broadcast('formReset'); + $globalMessages.addMessage('success', 'Saved successfully.', true); + if (stayInEdit) { + $state.go('^.edit', {id: data.id}); + } else { + $state.go('^.detail', {id: data.id}); + } + }); + }; + + try { + // extend with custom controller + $controller(config.controller, { + $scope: $scope, + detail: config.detail, + parents: config.parents, + config: config + }); + } catch (unused) { + $log.info('Couldn\'t instantiate controller with name ' + config.controller); + } + + // set edit object with parent relations + $scope.editDetail = $scope.detail; + if (_.isFunction($scope.editDetail.setParent)) { + $scope.editDetail.setParent(config.parents[0]); + } +} diff --git a/src/main/javascript/controller/cat-base-list-controller.js b/src/main/javascript/controller/cat-base-list-controller.js index 27c6cb2..86274ea 100644 --- a/src/main/javascript/controller/cat-base-list-controller.js +++ b/src/main/javascript/controller/cat-base-list-controller.js @@ -1,6 +1,5 @@ 'use strict'; - /** * @ngdoc controller * @name cat.controller.base.list:CatBaseListController @@ -25,8 +24,9 @@ * @param {object} catBreadcrumbsService catBreadcrumbsService * @param {object} catListDataLoadingService catListDataLoadingService * @param {object} config holds data like the listData object, the template url, base url, the model constructor, etc. + * @param {object} $globalMessages $globalMessages */ -function CatBaseListController($scope, $state, $controller, $log, catBreadcrumbsService, catListDataLoadingService, config) { +function CatBaseListController($scope, $state, $controller, $log, catBreadcrumbsService, catListDataLoadingService, $globalMessages, config) { if (!_.isUndefined(config.listData)) { this.titleKey = 'cc.catalysts.cat-breadcrumbs.entry.' + config.listData.endpoint.getEndpointName(); @@ -58,6 +58,7 @@ function CatBaseListController($scope, $state, $controller, $log, catBreadcrumbs this.remove = function (id) { config.listData.endpoint.remove(id) .then(function () { + $globalMessages.addMessage('success', 'Successfully deleted entry.', true); catListDataLoadingService.load(config.listData.endpoint, config.listData.searchRequest).then( function (data) { _.assign($scope.listData, data); @@ -77,4 +78,4 @@ function CatBaseListController($scope, $state, $controller, $log, catBreadcrumbs angular.module('cat.controller.base.list', ['cat.service.breadcrumbs']) .controller('CatBaseListController', - ['$scope', '$state', '$controller', '$log', 'catBreadcrumbsService', 'catListDataLoadingService', 'config', CatBaseListController]); + ['$scope', '$state', '$controller', '$log', 'catBreadcrumbsService', 'catListDataLoadingService', '$globalMessages', 'config', CatBaseListController]); diff --git a/src/main/javascript/service/cat-route-service.js b/src/main/javascript/service/cat-route-service.js index 9ce2027..771aaa3 100644 --- a/src/main/javascript/service/cat-route-service.js +++ b/src/main/javascript/service/cat-route-service.js @@ -1,5 +1,12 @@ 'use strict'; +angular.module('cat.service.route', [ + 'ui.router', + 'cat.service.message', + 'cat.service.breadcrumbs', + 'cat.service.validation' +]) + /** * @ngdoc service * @name cat.service.route:catRouteServiceProvider @@ -7,6 +14,21 @@ * This service provider delegates to the $stateProvider and actually creates 2 separate routes after applying various * conventions / defaults */ + .provider('catRouteService', CatRouteServiceProvider) + + .run(['$rootScope', '$log', '$globalMessages', 'catBreadcrumbsService', 'catValidationService', + function ($rootScope, $log, $globalMessages, catBreadcrumbsService, catValidationService) { + $rootScope.$on('$stateChangeError', function () { + var exception = arguments[arguments.length - 1]; + $globalMessages.addMessage('warning', exception); + $log.warn(exception); + }); + $rootScope.$on('$stateChangeSuccess', function () { + catBreadcrumbsService.clear(); + catValidationService.clearValidationErrors(); + }); + }]); + function CatRouteServiceProvider($stateProvider) { var viewNames = []; @@ -54,6 +76,20 @@ function CatRouteServiceProvider($stateProvider) { } } + function _registerCreateState(config, name) { + var stateName = _getStateName(name, config); + var createConfig = _getCreateConfig(config, name); + $stateProvider + .state(stateName + '.create', createConfig); + } + + function _registerEditState(config, name) { + var stateName = _getStateName(name, config); + var editConfig = _getEditConfig(config, name); + $stateProvider + .state(stateName + '.edit', editConfig); + } + function _registerListState(config, name) { var stateName = _getStateName(name, config); var listConfig = _getListConfig(config, name); @@ -87,6 +123,46 @@ function CatRouteServiceProvider($stateProvider) { }; } + /** + * A helper function which basically just overrides the controller, url and template of the detail config. + * @param config + * @param name + * @returns {{templateUrl: (string), controller: string, reloadOnSearch: (boolean), resolve: {config: (object)}}} + * @private + */ + function _getCreateConfig(config, name) { + var _config = _.assign({name: name}, config); + var detailConfig = _getEditConfig(config, name); + + detailConfig.url = _config.url || '/new'; + + return detailConfig; + } + + /** + * A helper function which basically just overrides the controller, url and template of the detail config. + * @param config + * @param name + * @returns {{templateUrl: (string), controller: string, reloadOnSearch: (boolean), resolve: {config: (object)}}} + * @private + */ + function _getEditConfig(config, name) { + var _config = _.assign({name: name}, config); + + return { + url: _config.url || '/:id/edit', + templateUrl: _config.templateUrl || 'template/cat-base-edit.tpl.html', + controller: 'CatBaseEditController', + reloadOnSearch: _config.reloadOnSearch, + resolve: { + config: function ($stateParams, catViewConfigService) { + // TODO $stateParams needs to be passed from here because otherwise it's empty... + return catViewConfigService.getDetailConfig(_config, $stateParams); + } + } + }; + } + /** * A helper function for list routes which applies a few optimizations and some auto configuration. * In the current state it handles 4 settings: @@ -167,7 +243,9 @@ function CatRouteServiceProvider($stateProvider) { var listUrl = _getListUrl(baseUrl, name, config); _registerAbstractState(listUrl, stateName); + _registerCreateState(_.assign({}, config.create, viewData), name); _registerDetailState(_.assign({}, config.details, viewData), name); + _registerEditState(_.assign({}, config.list, viewData), name); _registerListState(_.assign({}, config.list, viewData), name); }; @@ -184,24 +262,3 @@ function CatRouteServiceProvider($stateProvider) { return viewNames; }; } - -angular - .module('cat.service.route', [ - 'ui.router', - 'cat.service.message', - 'cat.service.breadcrumbs', - 'cat.service.validation' - ]) - .provider('catRouteService', CatRouteServiceProvider) - .run(['$rootScope', '$log', '$globalMessages', 'catBreadcrumbsService', 'catValidationService', - function ($rootScope, $log, $globalMessages, catBreadcrumbsService, catValidationService) { - $rootScope.$on('$stateChangeError', function () { - var exception = arguments[arguments.length - 1]; - $globalMessages.addMessage('warning', exception); - $log.warn(exception); - }); - $rootScope.$on('$stateChangeSuccess', function () { - catBreadcrumbsService.clear(); - catValidationService.clearValidationErrors(); - }); - }]); \ No newline at end of file diff --git a/src/main/javascript/service/cat-view-config-service.js b/src/main/javascript/service/cat-view-config-service.js index 093b402..c333cc1 100644 --- a/src/main/javascript/service/cat-view-config-service.js +++ b/src/main/javascript/service/cat-view-config-service.js @@ -16,7 +16,7 @@ function CatViewConfigService($q, catApiService, catListDataLoadingService) { var detailPromise; var detailId = $stateParams.id; - if (detailId === 'new') { + if (detailId === 'new' || detailId === undefined) { detailPromise = $q.when(new Model()); } else { detailPromise = endpoint.get(detailId); diff --git a/src/main/resources/template/cat-base-detail.tpl.html b/src/main/resources/template/cat-base-detail.tpl.html index 1609fcb..0041ff7 100644 --- a/src/main/resources/template/cat-base-detail.tpl.html +++ b/src/main/resources/template/cat-base-detail.tpl.html @@ -4,7 +4,7 @@
-
+
+
  New
-
{{title()}}
-
New
+
{{title()}}
-
-
-
- -
-
-
-
\ No newline at end of file diff --git a/src/main/resources/template/cat-base-edit.tpl.html b/src/main/resources/template/cat-base-edit.tpl.html new file mode 100644 index 0000000..38f6311 --- /dev/null +++ b/src/main/resources/template/cat-base-edit.tpl.html @@ -0,0 +1,27 @@ +
+ {{parent.title}} +
+ +
+
+
{{title()}}
+
New
+
+
+
+
+
+
+ +
+
\ No newline at end of file diff --git a/src/test/javascript/service/cat-view-config-service.spec.js b/src/test/javascript/service/cat-view-config-service.spec.js index 2967cd8..55e8aaa 100644 --- a/src/test/javascript/service/cat-view-config-service.spec.js +++ b/src/test/javascript/service/cat-view-config-service.spec.js @@ -96,26 +96,26 @@ describe('CatViewConfigService', function () { describe('.getDetailConfig', function () { it('should correctly initialize name and model', function () { - testDetailConfig({name: 'Test', model: 'model'}, {}, function (config) { + testDetailConfig({name: 'Test', model: 'model'}, {id: 42}, function (config) { expect(config.Model).toEqual('model'); expect(config.name).toEqual('Test'); }); }); it('should correctly apply controller', function () { - testDetailConfig({name: 'Test', model: 'model', controller: 'CTRL'}, {}, function (config) { + testDetailConfig({name: 'Test', model: 'model', controller: 'CTRL'}, {id: 42}, function (config) { expect(config.controller).toEqual('CTRL'); }); }); it('should correctly apply default controller', function () { - testDetailConfig({name: 'Test', model: 'model'}, {}, function (config) { + testDetailConfig({name: 'Test', model: 'model'}, {id: 42}, function (config) { expect(config.controller).toEqual('TestDetailsController'); }); }); it('should apply correct endpoint', function () { - testDetailConfig({name: 'Test', model: 'model', endpoint: 'endpoint'}, {}, function (config) { + testDetailConfig({name: 'Test', model: 'model', endpoint: 'endpoint'}, {id: 42}, function (config) { expect(config.endpoint).toEqual(mockEndpoint); }); }); @@ -128,7 +128,7 @@ describe('CatViewConfigService', function () { endpoint: { name: 'endpointname' } - }, {}, function (config) { + }, {id: 42}, function (config) { expect(config.endpoint).toEqual(mockEndpoint); }); }); @@ -143,6 +143,7 @@ describe('CatViewConfigService', function () { parents: ['parent1', 'parent2'] } }, { + id: 42, parent1Id: 1, parent2Id: 2 }, function (config) { @@ -158,7 +159,7 @@ describe('CatViewConfigService', function () { view: 'endpoint/endpoint-details-view.tpl.html' }; - testDetailConfig({name: 'Test', model: 'model', endpoint: 'endpoint'}, {}, function (config) { + testDetailConfig({name: 'Test', model: 'model', endpoint: 'endpoint'}, {id: 42}, function (config) { expect(config.templateUrls.view).toBe(resultObj.view); expect(config.templateUrls.edit).toBe(resultObj.edit); }); @@ -177,7 +178,9 @@ describe('CatViewConfigService', function () { model: 'model', endpoint: 'endpoint', additionalViewTemplate: true - }, {}, function (config) { + }, { + id: 42 + }, function (config) { expect(config.templateUrls.view.main).toBe(resultObj.view); }); @@ -187,7 +190,9 @@ describe('CatViewConfigService', function () { endpoint: 'endpoint', additionalViewTemplate: 'tabs', additionalViewTemplateTabs: 'result' - }, {}, function (config) { + }, { + id: 42 + }, function (config) { expect(config.tabs).toBe('result'); }); });