angularjs custom confirm box

46,581

Solution 1

I created a repo for this functionality. It wraps the ui-bootstrap modal to produce a confirmation box. It is customizable and easily integrated into any application.

Here is the link to the GitHub: https://github.com/Schlogen/angular-confirm

Example Usages:

As a directive:

 <button type="button" ng-click="delete()" confirm-if="checked" confirm="Are you sure, {{name}}?">Delete</button>

As a service:

$confirm({text: 'Are you sure you want to delete?'})
  .then(function() {
    $scope.deletedConfirm = 'Deleted';
  });

Solution 2

It seems to me that you're trying to do things the jQuery way from within the directive. However, what you want is as simple as pulling in the UI-Bootstrap directive for confirming actions. http://plnkr.co/edit/JhfAF1?p=preview

Solution 3

First simple service for modal windows:

app.service('ConfirmService', function($modal) {
  var service = {};
  service.open = function (text, onOk) {
    var modalInstance = $modal.open({
      templateUrl: 'myModalContent.html',
      controller: 'ModalConfirmCtrl',
      resolve: {
        text: function () {
          return text;
        }
      }
    });

    modalInstance.result.then(function (selectedItem) {
      onOk();
    }, function () {
    });
  };

  return service;
})

app.controller('ModalConfirmCtrl', function ($scope, $modalInstance, text) {

  $scope.text = text;

  $scope.ok = function () {
    $modalInstance.close(true);
  };

  $scope.cancel = function () {
    $modalInstance.dismiss('cancel');
  };
});

Then simple directive that uses it:

app.directive('confirm', function(ConfirmService) {
    return {
        restrict: 'A',
        scope: {
            eventHandler: '&ngClick'
        },
        link: function(scope, element, attrs){
          element.unbind("click");
          element.bind("click", function(e) {
            ConfirmService.open(attrs.confirm, scope.eventHandler);
          });
        }
    }
});

And here u go:

<button ng-click="test(12)" confirm='Are you sure?'>Button</button>

http://plnkr.co/edit/LOZOnsVyx3JU5XoKYn74?p=preview

Solution 4

Here is a nice directive for that.That is ngBootbox. Check it out.

<button class="btn btn-lg btn-primary"
        ng-bootbox-title="A cool title!"
        ng-bootbox-custom-dialog="Some custom text"
        ng-bootbox-buttons="customDialogButtons"
        ng-bootbox-class-name="some-class">
    Custom dialog
</button>

<script>
    $scope.customDialogButtons = {
        warning: {
            label: "Warning!",
            className: "btn-warning",
            callback: function() { $scope.addAction('Warning', false); }
        },
        success: {
            label: "Success!",
            className: "btn-success",
            callback: function() { $scope.addAction('Success!', true) }
        },
        danger: {
            label: "Danger!",
            className: "btn-danger",
            callback: function() { $scope.addAction('Danger!', false) }
        },
        main: {
            label: "Click ME!",
            className: "btn-primary",
            callback: function() { $scope.addAction('Main...!', true) }
        }
    };
    </script>

Demo

ngBootbox

Solution 5

To allow a button to be marked up like

<button type="button" ng-click="deleteItem(drink)" ng-confirm="Are you sure you want to delete '{{drink.name}}'">Delete</button>

You can write a directive that

  • Intercepts the click event before ngClick's click handler can run
  • Opens a dialog (using $modal and not the removed $dialog)
  • On close of the dialog (which is treated as a success) run the function specified by the ngClick attribute on the element.

Basing the code on the previous answer, you can do this as follows:

app.directive('ngConfirm', function($modal, $parse) {
  return {
    // So the link function is run before ngClick's, which has priority 0
    priority: -1,

    link: function(scope, element, attrs) {
      element.on('click', function(e) {
        // Don't run ngClick's handler
        e.stopImmediatePropagation();

        $modal.open({
          templateUrl: 'ng-confirm-template',
          controller: 'ngConfirmController',
          resolve: {
            message: function() {
              return attrs.ngConfirm;
            }
          }
        }).result.then(function() {
          // Pass original click as '$event', just like ngClick
          $parse(attrs.ngClick)(scope, {$event: e});
        });
      });
    }
  };
});

which needs a simple controller:

app.controller('ngConfirmController', function($scope, $modalInstance, message) {
  $scope.message = message;

  $scope.yes = function() {
    $modalInstance.close();
  };

  $scope.no = function() {
    $modalInstance.dismiss();
  };
});

and template for the dialog:

<script type="text/ng-template" id="ng-confirm-template">
  <div class="modal-body">
    <p>{{message}}</p>
  </div>
  <div class="modal-footer">
    <button class="btn btn-link pull-left" ng-click="no()">No</button>
    <button class="btn btn-primary pull-right" ng-click="yes()">Yes</button>
  </div>
</script>

You can see this running at http://plnkr.co/edit/Gm9lFsGb099w6kCMQoVY?p=preview

Edit: changed plunker link to example without scrollbar appearing/disappearing on display of the dialog

Share:
46,581
James Kleeh
Author by

James Kleeh

Member of the Grails Core team https://grails.org

Updated on July 09, 2022

Comments

  • James Kleeh
    James Kleeh almost 2 years

    So I'm trying to implement a custom confirm box using Angular. Ideally, I would simply like to add an attribute to enable the functionality. Example:

    <button type="button" ng-click="delete(foo)">Delete</button>  ->  <button type="button" ng-click="delete(foo)" ng-confirm="Are you sure you want to delete this foo?">Delete</button>
    

    (foo is inside an ng-repeat... foo in fooList..)

    So all of the problems I am having revolve around tying the click event that would normally happen to a different button. I have a seperate directive "confirmBox" that will create my modal (not using bootstrap) and handle all of the showing/hiding/etc.

    What I am currently using requires me to alter my ng-click functionality, which I really want to get away from:

    Current Implementation:

    <button ... ng-click="confirm('Are you sure you want to delete this foo?, 'delete', foo)">Delete</button>
    
    var confirmModule = angular.module('confirm', []);
    
    confirmModule.run(function($rootScope) {
        $rootScope.confirm = function(text, func, obj) {
            $rootScope.$broadcast('confirm', func, obj, text);
        };
    });
    
    confirmModule.directive('confirmBox', function($parse) {
    
        return {
            restrict: 'A',
            template: myModalTemplate,
            link: function(scope, element, attrs){
                element.hide();
                var noBtn = element.find("[name='no']");
                noBtn.bind("click", function() {
                    element.hide();
                });
                scope.$on("confirm", function(event, func, obj, text) {
                    var yesBtn = element.find("[name='yes']");
                    element.show();
                    yesBtn.unbind("click").bind("click", function() {
                        scope[func](obj);
                    });
                });
            }
        }
    });
    

    Anyone have any ideas? I started by adding the directive for the button and then unbinding the click event so ng-click doesn't fire. Then I am left with the string 'delete(foo)' from the ng-click attribute that I can execute with $parse(attrs.ngClick)(scope), but I don't know how to tie that to the separate directives button click.

    Edit: Here is a fiddle with my current attempt at implementation. The problem is the variable being passed in to the function is always undefined.

    http://jsfiddle.net/UCtbj/2/

    Edit2: Updated implementation, however I don't particularly like how it links the two directives together by targetting the other directives elements.

    http://jsfiddle.net/UCtbj/3/

  • James Kleeh
    James Kleeh about 11 years
    The problem with this is that it requires you to put this dialog code in each function you want it to show. It really has nothing to do with the business logic of the application so imo, it shouldn't be there.
  • ScottGuymer
    ScottGuymer about 10 years
    It seems that angular UI has been refactored and the $dialog service has been removed and you can use messagebox functionality as indicated in the plunker anymore. stackoverflow.com/questions/19468127/…
  • James Kleeh
    James Kleeh about 10 years
    I could refactor it to use the $modal service in my confirm directive. I still don't want to use $modal in my controller - its view logic so doesn't belong there.
  • James Kleeh
    James Kleeh over 9 years
    This implementation has the same idea as mine, however I can see some issues with it - see my answer.
  • Michal Charemza
    Michal Charemza over 9 years
    @JamesKleeh What are the issues?
  • Demodave
    Demodave over 9 years
    This one is pretty close but sort of jumps because it alters the scroll instead of doing an overlay.
  • Demodave
    Demodave over 9 years
    This one looks cleaner than mine. Does this one still need the && confirm test within ng-click? Or is it binded somehow?
  • MartianMartian
    MartianMartian almost 8 years
    is there a way to use ONLY the UI-BS part of the code and not having to load the rest?
  • MartianMartian
    MartianMartian almost 8 years
    is there a way to not use the ui-bootstrap?