This repository has been archived by the owner on May 29, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 6.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(dialog): add dialog functionality
- Loading branch information
1 parent
dc04547
commit 9c06f62
Showing
8 changed files
with
752 additions
and
0 deletions.
There are no files selected for viewing
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
# $dialogProvider <small>(service in ui.bootstrap)</small> | ||
|
||
## Description | ||
|
||
Used for configuring global options for dialogs. | ||
|
||
### Methods | ||
|
||
#### `options(opts)` | ||
|
||
Sets the default global options for your application. Options can be overridden when opening dialogs. Available options are: | ||
|
||
* `backdrop`: a boolean value indicating whether a backdrop should be used or not. | ||
* `modalClass`: the css class for the modal div, defaults to 'modal' | ||
* `backdropClass`: the css class for the backdrop, defaults to 'modal-backdrop' | ||
* `transitionClass`: the css class that applies transitions to the nodal and backdrop, defaults to 'fade' | ||
* `triggerClass`: the css class that triggers the transitions. default to 'in' | ||
* `resolve`: members that will be resolved and passed to the controller as locals | ||
* `controller`: the controller to associate with the included partial view | ||
* `backdropFade`: a boolean value indicating whether the backdrop should fade in and out using a CSS transition, defaults to false | ||
* `modalFade`: a boolean value indicating whether the nodal should fade in and out using a CSS transition, defaults to false | ||
* `keyboard`: indicates whether the dialog should be closable by hitting the ESC key, defaults to true | ||
* `backdropClick`: indicates whether the dialog should be closable by clicking the backdrop area, defaults to true | ||
|
||
Example: | ||
|
||
var app = angular.module('App', ['ui.bootstrap.dialog'] , function($dialogProvider){ | ||
$dialogProvider.options({backdropClick: false, modalFade: true}); | ||
}); | ||
|
||
# $dialog service | ||
|
||
## Description | ||
|
||
Allows you to open dialogs from within you controller. | ||
|
||
### Methods | ||
|
||
#### `dialog([templateUrl[, controller]])` | ||
|
||
Creates a new dialog, optionally setting the `templateUrl`, and `controller` options. | ||
|
||
Example: | ||
|
||
app.controller('MainCtrl', function($dialog, $scope) { | ||
$scope.openItemEditor = function(item){ | ||
var d = $dialog.dialog({modalFade: false, resolve: {item: angular.copy(item) }}); | ||
d.open('dialogs/item-editor.html', 'EditItemController'); | ||
}; | ||
}); | ||
|
||
// note that the resolved item as well as the dialog are injected in the dialog's controller | ||
app.controller('EditItemController', ['$scope', 'dialog', 'item', function($scope, dialog, item){ | ||
$scope.item = item; | ||
$scope.submit = function(){ | ||
dialog.close('ok'); | ||
}; | ||
}]); | ||
|
||
#### `message(title, message, buttons)` | ||
|
||
Opens a message box with the specified `title`, `message` ang a series of `buttons` can be provided, every button can specify: | ||
|
||
* `label`: the label of the button | ||
* `result`: the result used to invoke the close method of the dialog | ||
* `cssClass`: optinal, the CSS class (e.g. btn-primary) to apply to the button | ||
|
||
Example: | ||
|
||
app.controller('MainCtrl', function($dialog, $scope) { | ||
$scope.deleteItem = function(item){ | ||
var msgbox = $dialog.message('Delete Item', 'Are you sure?', [{label:'Yes, I'm sure, result: 'yes'},{label:'Nope', result: 'no'}]); | ||
msgbox.open().then(function(result){ | ||
if(result === 'yes') {deleteItem(item);} | ||
}); | ||
}; | ||
}); | ||
|
||
## Dialog class | ||
|
||
The dialog object returned by the `$dialog` service methods `open` and `message`. | ||
|
||
### Methods | ||
|
||
#### `open` | ||
|
||
(Re)Opens the dialog and returns a promise. | ||
|
||
#### `close([result])` | ||
|
||
Closes the dialog. Optionally a result can be specified. The result is used to resolve the promise returned by the `open` method. | ||
|
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,267 @@ | ||
// The `$dialogProvider` can be used to configure global defaults for your | ||
// `$dialog` service. | ||
var dialogModule = angular.module('ui.bootstrap.dialog', ['ui.bootstrap.transition']); | ||
|
||
dialogModule.controller('MessageBoxController', ['$scope', 'dialog', 'model', function($scope, dialog, model){ | ||
$scope.title = model.title; | ||
$scope.message = model.message; | ||
$scope.buttons = model.buttons; | ||
$scope.close = function(res){ | ||
dialog.close(res); | ||
}; | ||
}]); | ||
|
||
dialogModule.provider("$dialog", function(){ | ||
|
||
// The default options for all dialogs. | ||
var defaults = { | ||
backdrop: true, | ||
modalClass: 'modal', | ||
backdropClass: 'modal-backdrop', | ||
transitionClass: 'fade', | ||
triggerClass: 'in', | ||
resolve:{}, | ||
backdropFade: false, | ||
modalFade:false, | ||
keyboard: true, // close with esc key | ||
backdropClick: true // only in conjunction with backdrop=true | ||
/* other options: template, templateUrl, controller */ | ||
}; | ||
|
||
var globalOptions = {}; | ||
|
||
// The `options({})` allows global configuration of all dialogs in the application. | ||
// | ||
// var app = angular.module('App', ['ui.bootstrap.dialog'], function($dialogProvider){ | ||
// // don't close dialog when backdrop is clicked by default | ||
// $dialogProvider.options({backdropClick: false}); | ||
// }); | ||
this.options = function(value){ | ||
globalOptions = value; | ||
}; | ||
|
||
// Returns the actual `$dialog` service that is injected in controllers | ||
this.$get = ["$http", "$document", "$compile", "$rootScope", "$controller", "$templateCache", "$q", "$transition", | ||
function ($http, $document, $compile, $rootScope, $controller, $templateCache, $q, $transition) { | ||
|
||
var body = $document.find('body'); | ||
|
||
function createElement(clazz) { | ||
var el = angular.element("<div>"); | ||
el.addClass(clazz); | ||
return el; | ||
} | ||
|
||
// The `Dialog` class represents a modal dialog. The dialog class can be invoked by providing an options object | ||
// containing at lest template or templateUrl and controller: | ||
// | ||
// var d = new Dialog({templateUrl: 'foo.html', controller: 'BarController'}); | ||
// | ||
// Dialogs can also be created using templateUrl and controller as distinct arguments: | ||
// | ||
// var d = new Dialog('path/to/dialog.html', MyDialogController); | ||
function Dialog(opts) { | ||
|
||
var self = this, options = this.options = angular.extend({}, defaults, globalOptions, opts); | ||
|
||
this.backdropEl = createElement(options.backdropClass); | ||
if(options.backdropFade){ | ||
this.backdropEl.addClass(options.transitionClass); | ||
this.backdropEl.removeClass(options.triggerClass); | ||
} | ||
|
||
this.modalEl = createElement(options.modalClass); | ||
if(options.modalFade){ | ||
this.modalEl.addClass(options.transitionClass); | ||
this.modalEl.removeClass(options.triggerClass); | ||
} | ||
|
||
this.handledEscapeKey = function(e) { | ||
if (e.which === 27) { | ||
self.close(); | ||
e.preventDefault(); | ||
self.$scope.$apply(); | ||
} | ||
}; | ||
|
||
this.handleBackDropClick = function(e) { | ||
self.close(); | ||
e.preventDefault(); | ||
self.$scope.$apply(); | ||
}; | ||
} | ||
|
||
// The `isOpen()` method returns wether the dialog is currently visible. | ||
Dialog.prototype.isOpen = function(){ | ||
return this._open; | ||
}; | ||
|
||
// The `open(templateUrl, controller)` method opens the dialog. | ||
// Use the `templateUrl` and `controller` arguments if specifying them at dialog creation time is not desired. | ||
Dialog.prototype.open = function(templateUrl, controller){ | ||
var self = this, options = this.options; | ||
|
||
if(templateUrl){ | ||
options.templateUrl = templateUrl; | ||
} | ||
if(controller){ | ||
options.controller = controller; | ||
} | ||
|
||
if(!(options.template || options.templateUrl)) { | ||
throw new Error('Dialog.open expected template or templateUrl, neither found. Use options or open method to specify them.'); | ||
} | ||
|
||
this._loadResolves().then(function(locals) { | ||
var $scope = locals.$scope = self.$scope = $rootScope.$new(); | ||
|
||
self.modalEl.html(locals.$template); | ||
|
||
if (self.options.controller) { | ||
var ctrl = $controller(self.options.controller, locals); | ||
self.modalEl.contents().data('ngControllerController', ctrl); | ||
} | ||
|
||
$compile(self.modalEl.contents())($scope); | ||
self._addElementsToDom(); | ||
|
||
// trigger tranisitions | ||
setTimeout(function(){ | ||
if(self.options.modalFade){ self.modalEl.addClass(self.options.triggerClass); } | ||
if(self.options.backdropFade){ self.backdropEl.addClass(self.options.triggerClass); } | ||
}); | ||
|
||
self._bindEvents(); | ||
}); | ||
|
||
this.deferred = $q.defer(); | ||
return this.deferred.promise; | ||
}; | ||
|
||
// closes the dialog and resolves the promise returned by the `open` method with the specified result. | ||
Dialog.prototype.close = function(result){ | ||
var self = this; | ||
var fadingElements = this._getFadingElements(); | ||
|
||
if(fadingElements.length > 0){ | ||
for (var i = fadingElements.length - 1; i >= 0; i--) { | ||
$transition(fadingElements[i], removeTriggerClass).then(onCloseComplete); | ||
} | ||
return; | ||
} | ||
|
||
this._onCloseComplete(result); | ||
|
||
function removeTriggerClass(el){ | ||
el.removeClass(self.options.triggerClass); | ||
} | ||
|
||
function onCloseComplete(){ | ||
if(self._open){ | ||
self._onCloseComplete(result); | ||
} | ||
} | ||
}; | ||
|
||
Dialog.prototype._getFadingElements = function(){ | ||
var elements = []; | ||
if(this.options.modalFade){ | ||
elements.push(this.modalEl); | ||
} | ||
if(this.options.backdropFade){ | ||
elements.push(this.backdropEl); | ||
} | ||
|
||
return elements; | ||
}; | ||
|
||
Dialog.prototype._fadingEnabled = function(){ | ||
return this.options.modalFade || this.options.backdropFade; | ||
}; | ||
|
||
Dialog.prototype._bindEvents = function() { | ||
if(this.options.keyboard){ body.bind('keydown', this.handledEscapeKey); } | ||
if(this.options.backdrop && this.options.backdropClick){ this.backdropEl.bind('click', this.handleBackDropClick); } | ||
}; | ||
|
||
Dialog.prototype._unbindEvents = function() { | ||
if(this.options.keyboard){ body.unbind('keydown', this.handledEscapeKey); } | ||
if(this.options.backdrop && this.options.backdropClick){ this.backdropEl.unbind('click', this.handleBackDropClick); } | ||
}; | ||
|
||
Dialog.prototype._onCloseComplete = function(result) { | ||
this._removeElementsFromDom(); | ||
this._unbindEvents(); | ||
|
||
this.deferred.resolve(result); | ||
|
||
if(this._fadingEnabled()){ | ||
this.$scope.$apply(); | ||
} | ||
}; | ||
|
||
Dialog.prototype._addElementsToDom = function(){ | ||
body.append(this.modalEl); | ||
if(this.options.backdrop) { body.append(this.backdropEl); } | ||
this._open = true; | ||
}; | ||
|
||
Dialog.prototype._removeElementsFromDom = function(){ | ||
this.modalEl.remove(); | ||
if(this.options.backdrop) { this.backdropEl.remove(); } | ||
this._open = false; | ||
}; | ||
|
||
// Loads all `options.resolve` members to be used as locals for the controller associated with the dialog. | ||
Dialog.prototype._loadResolves = function(){ | ||
var values = [], keys = [], template, self = this; | ||
|
||
if (template = this.options.template) { | ||
} else if (template = this.options.templateUrl) { | ||
template = $http.get(this.options.templateUrl, {cache:$templateCache}) | ||
.then(function(response) { return response.data; }); | ||
} | ||
|
||
angular.forEach(this.options.resolve || [], function(value, key) { | ||
keys.push(key); | ||
values.push(value); | ||
}); | ||
|
||
keys.push('$template'); | ||
values.push(template); | ||
|
||
return $q.all(values).then(function(values) { | ||
var locals = {}; | ||
angular.forEach(values, function(value, index) { | ||
locals[keys[index]] = value; | ||
}); | ||
locals.dialog = self; | ||
return locals; | ||
}); | ||
}; | ||
|
||
// The actual `$dialog` service that is injected in controllers. | ||
return { | ||
// Creates a new `Dialog` with the specified options. | ||
dialog: function(opts){ | ||
return new Dialog(opts); | ||
}, | ||
// creates a new `Dialog` tied to the default message box template and controller. | ||
// | ||
// Arguments `title` and `message` are rendered in the modal header and body sections respectively. | ||
// The `buttons` array holds an object with the following members for each button to include in the | ||
// modal footer section: | ||
// | ||
// * `result`: the result to pass to the `close` method of the dialog when the button is clicked | ||
// * `label`: the label of the button | ||
// * `cssClass`: additional css class(es) to apply to the button for styling | ||
messageBox: function(title, message, buttons){ | ||
return new Dialog({templateUrl: 'template/dialog/message.html', controller: 'MessageBoxController', resolve: {model: { | ||
title: title, | ||
message: message, | ||
buttons: buttons | ||
}}}); | ||
} | ||
}; | ||
}]; | ||
}); |
Oops, something went wrong.