Soccertrash
Soccertrash

Reputation: 1901

Intercepting ng-click in angularJS

is it possible to write an interceptor for ng-click? I have a button or a link that leads to a deletion of an object in the backend. I'd like to have a confirmation dialog (modal) by just adding an attribute to the button/link. E.g.:

<a href="#" ng-click="deleteIt(id)" confirmation-needed>Delete</a>

Is this possible with AngularJS? Is there a better approach do solve this issue?

EDIT The deleteIt method resides in different controllers.

Thx

Upvotes: 21

Views: 21897

Answers (4)

Piran
Piran

Reputation: 7318

I've put an example directive in:

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

I achieve it by creating a directive:

  1. with a higher priority than ngClick, so that it gets called before ngClick,
  2. making that terminal so that it doesn't call ngClick.
  3. listening to click events, and then evaluating the ngClick value if the message is ok.

As a bonus, you can pass in your own message, such as:

<a href="#" ng-click="deleteIt(id)" 
    confirmation-needed="Really Delete?"
        >Delete with custom message</a>

The code looks like this:

app.directive('confirmationNeeded', function () {
  return {
    priority: 1,
    terminal: true,
    link: function (scope, element, attr) {
      var msg = attr.confirmationNeeded || "Are you sure?";
      var clickAction = attr.ngClick;
      element.bind('click',function () {
        if ( window.confirm(msg) ) {
          scope.$eval(clickAction)
        }
      });
    }
  };
});

Upvotes: 50

Dustin
Dustin

Reputation: 846

I played with this for a long time, but the async nature of Angular makes it tough to count on every directive playing nicely together. Then, I saw @tosh's answer, which got me thinking.

I think this combines several advantages: Create a Directive and Prepend ngClick

So, just add this directive to your app, and then add the "confirm-click" attribute to your link, as well as the confirmClick() function to your ngClick.

Link:

<a href="#" ng-click="confirmClick() && deleteIt(id)" confirm-click>Delete</a>

Directive:

.directive('confirmClick', function() {
    return {
        link: function (scope, element, attrs) {
            // setup a confirmation action on the scope
            scope.confirmClick = function(msg) {
                // msg can be passed directly to confirmClick('are you sure?') in ng-click
                // or through the confirm-click attribute on the <a confirm-click="Are you sure?"></a>
                msg = msg || attrs.confirmClick || 'Are you sure?';
                // return true/false to continue/stop the ng-click
                return confirm(msg);
            }
        }
    }
})

This is very similar to injecting the standard confirm() dialog, but because you have it in a directive, you can customize how you choose to run your confirm dialog (maybe a pretty modal, instead of a window dialog).

**** BONUS ANSWER ****

I played with this more and integrated a solution that uses a modal window as the confirmation, rather than the default window. Here's the complete solution: https://stackoverflow.com/a/23718694/1135826

Upvotes: 5

samson
samson

Reputation: 1152

I've given up on getting Piran's answer to work as I'd like it to. Instead, I've started just replacing ngClick with my own directive:

.directive('confirmClick', function() {
    return {
        restrict: 'A',
        link: function(scope, elt, attrs) {
            elt.bind('click', function(e) {
                var message = attrs.confirmation || "Are you sure you want to do that?";
                if (window.confirm(message)) {
                    var action = attrs.confirmClick;
                    if (action)
                        scope.$apply(scope.$eval(action));
                }
            });
        },
    };
})

This directive doesn't even have its own scope, which simplifies combining it with other directives significantly. It takes everything it needs directly from the attrs. It's used like so:

<span ng-show="editing" confirm-click="delete()" confirmation="delete confirmation message goes here">some text</span>

The delete() function must exist somewhere higher up in the $scope chain. I'm sure this could be improved by taking a closer look at how ng-click is implemented, but I haven't gotten around to it yet!

Upvotes: 10

Tosh
Tosh

Reputation: 36030

You can inject $window into your controller so that you can use $window.confirm from your scope, then:

<a href="#" ng-click="confirm('message') && deleteIt(id)">Delete</a>

Upvotes: 3

Related Questions