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.

Customer Management App

comments powered by Disqus

13 Comments

  • So, I have integrated this idea into a test application (which is using Bootstrap 3) and when dialog is invoked, the background grey appears but the dialog never appears. Curious, what version of Bootstrap are you using in this example?

    I have debugged the service code and the .open method is indeed being called, but can quite figure out why the dialog is now shown

  • Mike,

    From what I've read they've been fighting some Bootstrap 3 issues....that's why they pulled the $dialog service. I'm currently sticking with Bootstrap 2.x since it doesn't cause any problems. I'm guessing there might be a CSS class or something along those lines that isn't getting set properly. Tough to say though.

    Dan

  • Thank you for this video introduction. Really a good one .

  • Can you provide an example of how to hook a custom controller in this service? I am very new to angular and cannot figure it out.
    Thanks

  • Please provide a working example. I had (have) trouble following it

    For example:

    "To use the modal service you’ll need to reference the modalService.js script"

    This is the first mention of 'modalService.js'

    Next, in the 'Using the Modal Service' section, the code is cut off on the right, making it hard to follow.

    Thanks

  • ...also the last example doesn't seem to be valid code. Where is the closing ']'?

  • Alan,

    Thanks - there was a ]) missing in the last example which was been fixed. As far as a working example, please see the information in the conclusion. It provides a link directly to a fully functional application that uses the service where you can grab the script if needed.

    Dan

  • Using bootstrap 3.0, I added this

    .modal {
    display: block;
    }

    to my .css the was displayed.

    Could you please provide an example of how to use a custom controller? I see that I can set up the modalDefaults hash { controller: myController }, but I can't seem to get myController to be able to access things in the scope of the Service (like $modalInstance and the tempModalOptions..etc).

    Has anyone done this before that could help me out?

  • Hi,

    I referred your last tutorial to create $dialogservice in our application. With the new ui-bootstrap version, $dialog is not available. I am trying to get the $modalService from this tutorial working but not able to get templateUrl to work as expected.

    The same template Url which worked in $dialogService now throws the following error in $modal implementation -
    'Error: Template must have exactly one root element. was: ' with black content.

    I updated to ui-bootstrap-tpls-0.6.0 but still on Angular Js version v1.0.7.

  • I have tried supplying modal defaults to the modal service and when i do the modal does not work correctly - none of the modal options are populating the template. However if I pass an empty object, {} the modal works - I looked at the customer manager project and see that none of the code samples in that project pass modalOptions to the service so I cant use this as proof that the service works with options as intended

  • karen:

    I'll take a look at it when I get a chance (won't be right away unfortunately). In the meantime if you get a chance to look into it more and find anything please let me know.

    Dan

  • Ill take a look at it - when you have a controller defined in your modal I cant find a way to pass the modalOptions in - in your code you do it in the controller definition - like so $scope.modalOptions = tempModalOptions;

    tempModalDefaults.controller = function ($scope, $modalInstance) {
    $scope.modalOptions = tempModalOptions;
    $scope.modalOptions.ok = function (result) {
    $modalInstance.close('ok');
    };
    $scope.modalOptions.close = function (result) {
    $modalInstance.close('cancel');
    };

    Im a relative newbie with angular and not sure how to do this - the scope is relative to the controller definition and cant figure out how to create a new scope to append the modaloptions to

  • Hi Dan,

    How can i set the Header and Body Text (If they are templates themselves). I have html files for these in partials folder. I need to assign the contents of those files into the modalOptions variables.

    Thanks

Comments have been disabled for this content.