Skip to main content
Home  ›  Blog

Non-Blocking "Are-You-Sure" Dialog Pattern with AngularJS (300)

In modern web UX we sometimes need to ask the user something - but it's also ok if the user ignores the question. An example of this is when the user accidentally clicks the background of a modal-dialog. Here's how to implement an elegant solution using AngularJS and Angular-Toastr.

Why we need this pattern

This is just an example, I'm sure you'll have your own situations. When using bootstrap-style dialogs and modal-dialogs, we often want to offer a "hit-escape" or "click on the background" to get away. Of course we must first check if the model is dirty(nothing changed) - to not loose data. 

Now in our experience, people would often click on the background by accident. For example, they would be marking text with a mouse and releasing the button on the background. Or they would open a drop-down and not-select something by clicking beside the drop-down. In these cases a JavaScript dialog blocking the UI is very annoying, because it interrupts the use-flow. 

The image to the side shows the alert as it used to be in the 2sxc-edit dialog before we refactored it. 

AngularJS Code to Provide this Functionality

// handle maybe-leave
vm.maybeLeave = {
    save: function() { vm.save(true); },
    quit: $scope.close,
    handleClick: function(event) {
        var target = event.target || event.srcElement;
        if (target.id === "save" || target.id === "quit") {
            vm.allowCloseWithoutAsking = true;
            vm.maybeLeave[target.id]();
        }
    },
    ask: function(e) {
        if (vm.allowCloseWithoutAsking)
            return;
        var template = "<div>"  // note: this variable must be inside this method, to ensure that translate is pre-loaded before we call it
            + $translate.instant("Errors.UnsavedChanges") + "<br>"
            + "<button type='button' id='save' class='btn btn-primary' ><i class='icon-ok'></i>" +  $translate.instant("General.Buttons.Save") + "</button> &nbsp;"
            + "<button type='button' id='quit' class='btn btn-default' ><i class= 'icon-cancel'></i>" + $translate.instant("General.Buttons.NotSave") + "</button>"
            + "</div>";
        if (vm.dialog && vm.dialog.isOpened)
            toastr.clear(vm.dialog);
        vm.dialog = toastr.warning(template, {
            allowHtml: true,
            timeOut: 3000,
            onShown: function (toast) {
                toast.el[0].onclick = vm.maybeLeave.handleClick;
            }
        });
        e.preventDefault();
    }
};
        
$scope.$on("modal.closing", vm.maybeLeave.ask);

The general Code Explained

In general the code works as follows

  1. Watch for messages related to closing the modal dialog
  2. If closing is attempted, show the toaster which will go away within 3 seconds
  3. Since creating a toastr is a delayed operation, we add an onShown event which will handle all clicks on the toastr
  4. Once the click happens, check if it was from one of our buttons...
  5. ...and then get out 

Daniel Mettler grew up in the jungles of Indonesia and is founder and CEO of 2sic internet solutions in Switzerland and Liechtenstein, an 20-head web specialist with over 800 DNN projects since 1999. He is also chief architect of 2sxc (see github), an open source module for creating attractive content and DNN Apps.

Read more posts by Daniel Mettler