Building an AngularJS Modal Service
In a previous post I walked through a custom AngularJS dialog service that can be used in projects to easily show dialogs to users like the following:
Since writing that post, the Angular UI Bootstrap project has pulled (hopefully temporarily) the $dialog service due to some incompatibility issues with the latest version of Bootstrap. Fortunately, the $modal service provided by Angular UI Bootstrap can be used in place of $dialog to accomplish the same type of task.
Creating a Modal Service
To make a custom modal service you first need to reference the Angular UI Bootstrap script in your main page:
<script src="Scripts/angular-ui-bootstrap.js"></script>
Next, define the shell HTML code for the modal dialog that you’d like to display and save it into an .html file. I named mine modal.html and placed it in the app/partials folder within my application. Here’s an example of a file I use:
<div class="modal-header"> <h3>{{modalOptions.headerText}}</h3> </div> <div class="modal-body"> <p>{{modalOptions.bodyText}}</p> </div> <div class="modal-footer"> <button type="button" class="btn"
data-ng-click="modalOptions.close()">{{modalOptions.closeButtonText}}</button> <button class="btn btn-primary"
data-ng-click="modalOptions.ok();">{{modalOptions.actionButtonText}}</button> </div>
You can see that the HTML relies on a modalOptions object (more on that later) to bind various properties into the modal dialog such as headerText, bodyText, closeButtonText, and actionButtonText. It also defines close() and ok() functions that will be called as the respective buttons are clicked.
Once the HTML is defined you can use the modal dialog service (named modalService). It looks like the following:
angular.module('yourModuleName').service('modalService', ['$modal', function ($modal) { var modalDefaults = { backdrop: true, keyboard: true, modalFade: true, templateUrl: '/app/partials/modal.html' }; var modalOptions = { closeButtonText: 'Close', actionButtonText: 'OK', headerText: 'Proceed?', bodyText: 'Perform this action?' }; this.showModal = function (customModalDefaults, customModalOptions) { if (!customModalDefaults) customModalDefaults = {}; customModalDefaults.backdrop = 'static'; return this.show(customModalDefaults, customModalOptions); }; this.show = function (customModalDefaults, customModalOptions) { //Create temp objects to work with since we're in a singleton service var tempModalDefaults = {}; var tempModalOptions = {}; //Map angular-ui modal custom defaults to modal defaults defined in service angular.extend(tempModalDefaults, modalDefaults, customModalDefaults); //Map modal.html $scope custom properties to defaults defined in service angular.extend(tempModalOptions, modalOptions, customModalOptions); if (!tempModalDefaults.controller) { tempModalDefaults.controller = function ($scope, $modalInstance) { $scope.modalOptions = tempModalOptions; $scope.modalOptions.ok = function (result) { $modalInstance.close(result); }; $scope.modalOptions.close = function (result) { $modalInstance.dismiss('cancel'); }; } } return $modal.open(tempModalDefaults).result; }; }]);
Looking through the code you’ll see two functions named showModal() and show(). showModal() sets the backdrop property to “static” so that it won’t go away when the user clicks on it (see the Angular UI Bootstrap documentation for more details on that property). It then calls show() which applies values that are supplied to the $modal service. The call to open() at the bottom of modalService handles displaying the modal dialog.
Using the Modal Service
To use the modal service you’ll need to reference the modalService.js script in your application and then inject it into a controller. Here’s an example of a CustomerEditController object that injects modalService along with several other dependencies:
app.module('yourModuleName').controller('CustomerEditController', ['$scope', '$location',
'$routeParams', '$timeout', 'config', 'dataService', 'modalService', function ($scope, $location, $routeParams, $timeout, config,
dataService, modalService) {
}]);
Next you’ll need to define the modal options that you’d like to use for the header text, body text, and button text. Once the options are defined you can call the modalService’s showModal() function as shown next. It takes any custom modal defaults you supply (an example of modal defaults can be seen in the modalService code) as well as the custom modal options.
app.module('yourModuleName').controller('CustomerEditController', ['$scope', '$location',
'$routeParams', '$timeout', 'config', 'dataService', 'modalService', function ($scope, $location, $routeParams, $timeout, config,
dataService, modalService) {
$scope.deleteCustomer = function () {
var custName = $scope.customer.firstName + ' ' + $scope.customer.lastName;
var modalOptions = { closeButtonText: 'Cancel', actionButtonText: 'Delete Customer', headerText: 'Delete ' + custName + '?', bodyText: 'Are you sure you want to delete this customer?' }; modalService.showModal({}, modalOptions).then(function (result) { dataService.deleteCustomer($scope.customer.id).then(function () { $location.path('/customers'); }, processError); }); }
}]);
If the user cancels the dialog it’ll disappear automatically. If they click the “OK” button (or whatever text you gave the button) you can process the request. In this example, the code deletes a customer by making a call to a dataService factory.
Conclusion
There’s even more you can do including supplying custom modal defaults which allows you to hook a custom controller to the modalService in situations where the modal dialog will display additional data or controls. Refer back to the modalService code above to get an idea how the controller concept works (see the show() method). To see the modalService in action check out the Customer Manager project I put together on Github.