Showing alert in angularjs when user leaves a page
Asked Answered
N

6

59

I'm an angularjs new bee. I'm trying to write a validation which alerts the user when he tries to close the browser window.

I have 2 links on my page v1 and v2.When clicked on the links it takes to the specific pages. Here is the code to redirect to v1 and v2

angular.module('myApp', ['myApp.filters', 'myApp.services', 'myApp.directives'])

.config(['$routeProvider', function($routeProvider) {
        $routeProvider.when('/v1', {templateUrl: 'pages/v_1.html', controller: MyCtrl1});
        $routeProvider.when('/v2', {templateUrl: 'pages/v_2.html', controller: MyCtrl2});
        $routeProvider.otherwise({redirectTo: '/v1'});
}]);

I want to pop up a message when the user clicks on v1 that "he's about to leave from v1, if he wishes to continue" and same on clicking on v2. Any pointers on how to achieve this would be appreciated.

I got an answer here but it pops up the message after every time interval.

Updated Code;

Controllers

function MyCtrl1() {
    $scope.$on('$locationChangeStart', function (event, next, current) {
        if ('your condition') {
            event.preventDefault();

            MessageService.showConfirmation(
                'Are you sure?',
            MessageService.MessageOptions.YES_NO, {
                'YES': function () {
                    blockNavigation = false;
                    $location.url($location.url(next).hash());
                    $rootScope.$apply();
                },
                'NO': function () {
                    MessageService.clear();
                    $log.log('NO Selected')
                }
            });
        }
    });
}
MyCtrl1.$inject = [];


function MyCtrl2() {}
MyCtrl2.$inject = [];
Nemato answered 11/2, 2013 at 9:55 Comment(1)
I believe the term you wanted is "newbie". Unless you're actually an apis mellifera pupa.Gray
G
106

The code for the confirmation dialogue can be written shorter this way:

$scope.$on('$locationChangeStart', function( event ) {
    var answer = confirm("Are you sure you want to leave this page?")
    if (!answer) {
        event.preventDefault();
    }
});
Grimsley answered 21/8, 2013 at 11:4 Comment(7)
this is a cleaner solution. The other one led me to an infinite loop of navigate confirmation...Willette
There also could be a handler for standard JavaScript event, that listens for resource unloading. I.e. when it is not a SPA app. The listener could be assigned to window.onbeforeunload.Persons
Sadly, this doesn't work when you are using route servce, the event is triggered everytime the location changes..Cheryl
When using Angular ui-router : use '$stateChangeStart' instead of '$locationChangeStart'Garland
I coupled this with a .$off extension that really help separate concernsPeacock
Am I the only one for whom this code comes up as soon as I open a page, instead of when I navigate away?Manganous
This works when navigating away, but not when refreshing the current page.Duet
A
45

Lets seperate your question, you are asking about two different things:

1.

I'm trying to write a validation which alerts the user when he tries to close the browser window.

2.

I want to pop up a message when the user clicks on v1 that "he's about to leave from v1, if he wishes to continue" and same on clicking on v2.

For the first question, do it this way:

window.onbeforeunload = function (event) {
  var message = 'Sure you want to leave?';
  if (typeof event == 'undefined') {
    event = window.event;
  }
  if (event) {
    event.returnValue = message;
  }
  return message;
}

And for the second question, do it this way:

You should handle the $locationChangeStart event in order to hook up to view transition event, so use this code to handle the transition validation in your controller/s:

function MyCtrl1($scope) {
    $scope.$on('$locationChangeStart', function(event) {
        var answer = confirm("Are you sure you want to leave this page?")
        if (!answer) {
            event.preventDefault();
        }
    });
}
Accredit answered 11/2, 2013 at 12:4 Comment(6)
I want an alert which notify the user that he's about to leave the page,does he really want to continue.Hope i'm clear.Nemato
where should i put this code.Sorry but I'm a complete angularjs new bie?Nemato
In the controller where you want to confirm the transition. in your case its MyCtrl1 and MyCtrl2 controllers.Accredit
in both the controllers.I have update my question as per ur code.have a lookNemato
you're are not injecting $scope to your controllers!Accredit
@YairNevet, you blew up Firefox!!! "e.preventDefault()" causes an infinite event loop... see Scheintod for a more robust version.Seibold
M
22

Here is the directive I use. It automatically cleans itself up when the form is unloaded. If you want to prevent the prompt from firing (e.g. because you successfully saved the form), call $scope.FORMNAME.$setPristine(), where FORMNAME is the name of the form you want to prevent from prompting.

.directive('dirtyTracking', [function () {
    return {
        restrict: 'A',
        link: function ($scope, $element, $attrs) {
            function isDirty() {
                var formObj = $scope[$element.attr('name')];
                return formObj && formObj.$pristine === false;
            }

            function areYouSurePrompt() {
                if (isDirty()) {
                    return 'You have unsaved changes. Are you sure you want to leave this page?';
                }
            }

            window.addEventListener('beforeunload', areYouSurePrompt);

            $element.bind("$destroy", function () {
                window.removeEventListener('beforeunload', areYouSurePrompt);
            });

            $scope.$on('$locationChangeStart', function (event) {
                var prompt = areYouSurePrompt();
                if (!event.defaultPrevented && prompt && !confirm(prompt)) {
                    event.preventDefault();
                }
            });
        }
    };
}]);
Mantelletta answered 4/4, 2014 at 15:11 Comment(2)
I had to use $(window).bind('beforeunload', doPrompt); rather than window.addEventListener('beforeunload', areYouSurePrompt); (and unbind for removeEventListener) in FF 31.0 for whatever reason as the native window calls weren't working (while window.onbeforeunload = function(){ return "sure?"; } worked)?! Also from what I can tell, the $locationChangeStart is never firing and considering the use of onbeforeunload is this block really necessary?Congratulate
@Congratulate upvoted for the tip, the code above also didn't work on Chrome42 I had to do the as you mentioned.Mammal
B
9

As you've discovered above, you can use a combination of window.onbeforeunload and $locationChangeStart to message the user. In addition, you can utilize ngForm.$dirty to only message the user when they have made changes.

I've written an angularjs directive that you can apply to any form that will automatically watch for changes and message the user if they reload the page or navigate away. @see https://github.com/facultymatt/angular-unsavedChanges

Hopefully you find this directive useful!

Ballistics answered 22/8, 2013 at 15:46 Comment(0)
O
2

The other examples here work fine for the old versions of ui-router (>=0.3.x) but all state events, such as $stateChangeStart, are deprecated as of 1.0. The new ui-router 1.0 code uses the $transitions service. So you need to inject $transitions into your component then use the $transitions.onBefore method as the code below demonstrates.

$transitions.onBefore({}, function(transition) {
  return confirm("Are you sure you want to leave this page?");
});

This is just a super simple example. The $transitions service can accept more complicated responses such as promises. See the HookResult type for more information.

Oahu answered 28/12, 2016 at 14:31 Comment(0)
N
-1
$scope.rtGo = function(){
            $window.sessionStorage.removeItem('message');
            $window.sessionStorage.removeItem('status');
        }

$scope.init = function () {
            $window.sessionStorage.removeItem('message');
            $window.sessionStorage.removeItem('status');
        };

Reload page: using init

Nerland answered 8/8, 2017 at 15:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.