Skip to content

Instantly share code, notes, and snippets.

@cef62
Forked from pswai/ng-strap-modal-decorator.js
Last active August 29, 2015 14:17
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 cef62/0f8ea91e3b0be8f87c24 to your computer and use it in GitHub Desktop.
Save cef62/0f8ea91e3b0be8f87c24 to your computer and use it in GitHub Desktop.
Decorate ng-strap modal component with an .open() method to support dynamic controller, resolved properties. A promise is returned from open(), resolving when user confirm the modal and rejecting when user cancel the modal.
(function () {
'use strict';
angular.module('app.views', [])
.controller('MainController', MainController);
function MainController($modal) {
// ---------------------------------
// Open Modal
// ---------------------------------
this.showModal = function showModal() {
var config = {
// normal ng-strap
animation: 'am-flip-x',
placement: 'center',
template: 'modal-confirm.tpl.html',
keyboard: false,
backdrop: 'static',
// decorated ng-strap
controller: 'ModalConfirmTemplate',
controllerAs: 'ctrl',
resolve: {
provider: function provider() {
return {
title: 'My Custom Title',
content: 'Hello Modal<br />This is a multiline message!'
};
},
otherData: function otherData() {
return {
prop1: 'value1',
prop2: 'value2',
prop3: 'value3'
};
}
}
};
/*
$modal.open() --> return a promise, resolved with a modal object
*/
$modal.open(config)
.then(
function modalOpened(modal) {
/*
modal has following fields:
{
modal: Original $modal response object,
promise: promise resolved used close() and cancel() methods,
close: close the modal and resolve the promise, accept a single argument to be passed to the promise
cancel: cancel the modal and rejet the promise, accept a reason to be passed to the promise
}
*/
modal.promise
.then(
function modalAccepted(result) {
console.log('modal completed', result);
}
)
.catch(
function modalRefused(reason) {
console.log('modal canceled: ' + reason);
}
);
}
);
};
}
})();
<div class="modal" tabindex="-1" role="dialog">
<div class="modal-dialog profile-delete-modal">
<div class="modal-content text-center">
<div class="modal-body" ng-show="ctrl.content">
<img src="./assets/images/bomb.svg" class="outcomeimgs" />
<h1 class="m-top-0 m-bottom-15 ">{{ctrl.title}} </h1>
<p ng-bind-html="ctrl.content"></p>
<div>
<h3>Other dynamic data</h3>
<ul>
<li ng-repeat="item in ctrl.otherData">{{item}}</li>
</ul>
</div>
</div>
<div class="row m-0 topline">
<div class="row m-0">
<button type="button" class="btn-square-big bg-red col-md-6 small-bold" ng-click="ctrl.cancel('modal dismissed')">No</button>
<button type="button" class="btn-square-big bg-black col-md-6 small-bold" ng-click="ctrl.confirm()">Yes</button>
</div>
</div>
</div>
</div>
(function () {
'use strict';
angular.module('app.modals', [)
/*
$modals.open() decorates the controller with 2 additional methods:
- ctrl.close(result)
- ctrl.cancel(reason)
*/
.controller('ModalConfirmTemplate',
function ModalConfirmTemplate(provider, otherData, userProfile) {
// userProfile is a standard injection
// provider, otherData are resolved injection
this.title = provider.title;
this.content = provider.content;
this.otherData = otherData;
this.confirm = function confirm() {
var result = {
name: userProfile.first_name + ' ' + userProfile.last_name,
email: userProfile.email
};
this.close(result);
};
}
);
})();
// Dependency: underscore.js or lodash
(function () {
'use strict';
angular.module('app.config')
.config(
function config($provide) {
// Use decorator to add new functionality
$provide.decorator('$modal',
function modalDecorator($controller, $delegate, $injector, $q, $rootScope) {
/*
* $modal.open() function
*
* This function adds new options to `$modal()`.
*
* New options:
* - controller {String|Function} First param of $controller. For string, controllerAs syntax is supported.
* - controllerAs {String} The 'as X' part of controllerAs syntax.
* - resolve {Object} Like the resolve in ngRoute
*
* Notes:
* -- Use either `controller: myCtrl as vm` or `controllerAs: vm`. Don't use both.
* -- Not sure if ngAnnotate supports this. It should since it understands '$modal.open()' in UI Bootstrap
*/
function open(config) {
var ctrl;
var resolvePromises = [];
var allDone;
var options = _.omit(config, ['controller', 'controllerAs', 'resolve']); // Options to be passed to $modal()
var modalScope = options.scope || $rootScope;
// Setup controller
if (config.controller) {
// Resolve
if (config.resolve) {
resolvePromises = _
.map(config.resolve,
function mapValues(resolveFunc) {
return $injector.invoke(resolveFunc);
}
);
}
allDone = $q.all(resolvePromises)
.then(
function allResolved(resolves) {
var locals = {};
// Assign resolves
var iter = 0;
_.forEach(config.resolve,
function each(resolveFunc, name) {
locals[name] = resolves[iter++];
}
);
// Create new scope
modalScope = modalScope.$new();
locals.$scope = modalScope;
// Instantiate controller
ctrl = $controller(config.controller, locals);
// manage controllerAs
if (config.controllerAs) {
modalScope[config.controllerAs] = ctrl;
}
}
);
} else {
allDone = $q.when(true);
}
return allDone.then(
function controllerReady() {
// Prepare final options
_.extend(options, {
scope: modalScope
});
var modal = $delegate(options);
var modalDefer = $q.defer();
// --------------------------------
// add extra methods to controller
// --------------------------------
// close resolving promise with return values
ctrl.close = function close(result) {
modalDefer.resolve(result);
modal.hide();
};
// close rejecting promise with specific reason
ctrl.cancel = function cancel(reason) {
modalDefer.reject(reason);
modal.hide();
};
return {
modal: modal,
promise: modalDefer.promise,
close: ctrl.close,
cancel: ctrl.cancel
};
}
);
}
// Add new open() method to the $modal service
$delegate.open = open;
return $delegate;
}
);
}
);
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment