app.factory('ModalHelper', ['$modal', function($modal) { var baseInjectables = ['$scope', '$modalInstance', '$saveForm', 'resourceAction', 'model']; /* * Helps with creation of modal windows. * * Dynamically constructs controller for the modal window. Uses `templateUrl` as controller's template. Allows * to pass a dictionary of resolves for $modal service. These resolves will be copied to the modal's scope once they * are resolved. * * Takes additional parameter `scopeFactories` which is a dictionary of factory functions. The result of calling * this functions will be copied to the modal's scope. `this` inside of those functions points to the * ModalController, so it's possible to use controller's functions (getModel, getResolve, ...). */ return function(templateUrl, additionalResolves, scopeFactories) { return new function(){ var self = this; function ModalController($scope, $modalInstance, $saveForm, resourceAction, model) { $scope.model = model; this.getModel = function() { return $scope.model; }; // copy resolves to the scope var resolves = []; for (var i = baseInjectables.length; i < arguments.length; i++) { resolves.push(arguments[i]); } Object.keys(self.updatedAdditionalResolves).forEach(function(resolveName, index) { $scope[resolveName] = resolves[index]; }); this.getResolve = function(name) { if (name in self.updatedAdditionalResolves && name in $scope) { return $scope[name]; } else { throw new Error("The resolve '" + name + "' wasn't copied to the modal's scope.") } }; var modalController = this; if (scopeFactories !== undefined) { Object.keys(scopeFactories).forEach(function(factoryName) { $scope[factoryName] = scopeFactories[factoryName].call(modalController); }); } $scope.cancel = function() { return $modalInstance.dismiss('cancel'); }; $scope.save = function() { $saveForm($scope, resourceAction.call(null, $scope.model).$promise, function (savedObject) { $modalInstance.close(savedObject); }); }; } this.openModal = function(model, resourceAction, updatedResolves) { var injectablesForController = baseInjectables.slice(); self.updatedAdditionalResolves = angular.extend(additionalResolves || {}, updatedResolves || {}); Object.keys(self.updatedAdditionalResolves).forEach(function(resolveName) { injectablesForController.push(resolveName); }); injectablesForController.push(ModalController); var resolveMap = angular.extend({ resourceAction: function() { return resourceAction; }, model: function() { return model; } }, self.updatedAdditionalResolves); return $modal.open({ templateUrl: templateUrl, resolve: resolveMap, controller: injectablesForController }) } }; }; }]); app.factory('ModalCrudHelper', ['$rootScope', function($rootScope) { return function(resource, modalHelper, signalPrefix) { function CrudHelper(resource, modalHelper, signalPrefix) { function broadcast(eventName, object) { if (signalPrefix !== undefined) { $rootScope.$broadcast(signalPrefix + '-' + eventName, object.id); } } this.addObject = function (model, objectsList, updatedResolves) { var modalInstance = modalHelper.openModal(model, resource.save, updatedResolves); return modalInstance.result.then(function (object) { objectsList.push(object); broadcast('created', object); }); }; this.editObject = function (object, objectsList, updatedResolves) { var objectIndex = objectsList.indexOf(object); var objectCopy = angular.copy(object); var modalInstance = modalHelper.openModal(objectCopy, resource.update, updatedResolves); return modalInstance.result.then(function (updatedObject) { if (objectIndex > -1) { objectsList.splice(objectIndex, 1, updatedObject); } broadcast('updated', updatedObject); }); }; this.removeObject = function (object, objectsList) { var index = objectsList.indexOf(object); return resource.delete({id: object.id}).$promise.then(function () { if (index > -1) { objectsList.splice(index, 1); } broadcast('deleted', object); }); }; } return new CrudHelper(resource, modalHelper, signalPrefix); } }]);