当用户离开页面时在angularjs中显示警报

59

我是一个刚接触angularjs的新手。我想写一个验证,当用户尝试关闭浏览器窗口时会弹出警告。

我的页面上有两个链接v1和v2,当点击这些链接时,它们会跳转到特定的页面。以下是重定向到v1和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'});
}]);

当用户点击v1或v2时,我想弹出一条消息,“他即将离开v1(或v2),如果他希望继续,请确认”。欢迎任何有关如何实现此功能的指导。

我在这里得到了答案here,但它会在每个时间间隔后弹出消息。

更新的代码:

控制器

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 = [];

12
我相信你想要的术语是“新手”。除非你真的是一个蜜蜂的蛹。请参考链接:http://www.urbandictionary.com/define.php?term=newbie - Sean the Bean
6个回答

106

确认对话框的代码可以以更简洁的方式编写:

$scope.$on('$locationChangeStart', function( event ) {
    var answer = confirm("Are you sure you want to leave this page?")
    if (!answer) {
        event.preventDefault();
    }
});

5
这是一个更清洁的解决方案。另一个让我陷入了无限循环的导航确认中... - wakandan
1
还可以为标准的JavaScript事件编写处理程序,以便监听资源卸载。例如,在非SPA应用程序中,可以将监听器分配给window.onbeforeunload - Eugene
5
遗憾的是,当使用路由服务时,这种方法不起作用。每次位置发生变化时都会触发事件。 - vdegenne
34
在使用Angular ui-router时,请使用'$stateChangeStart'而不是'$locationChangeStart'。 - OlivierLarue
3
我打开页面时,这段代码就立即出现了,而不是在我离开页面时才出现,难道只有我会出现这种情况吗? - Chris
显示剩余2条评论

45

让我们将您的问题拆分成两个部分:

1.

我正在尝试编写一个验证程序,在用户尝试关闭浏览器窗口时发出警报。

2.

我想在用户单击v1时弹出一个消息,提示“他即将离开v1,如果希望继续,请点击”,并且在单击v2时也是如此。

对于第一个问题,可以这样做:

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;
}

对于第二个问题,可以这样处理:

你需要处理$locationChangeStart事件以连接视图过渡事件,因此请使用以下代码在控制器中处理过渡验证:

function MyCtrl1($scope) {
    $scope.$on('$locationChangeStart', function(event) {
        var answer = confirm("Are you sure you want to leave this page?")
        if (!answer) {
            event.preventDefault();
        }
    });
}

我想要一个警告,提醒用户他即将离开页面,是否真的要继续。希望我表达清楚了。 - iJade
我应该把这段代码放在哪里?很抱歉,但我完全是AngularJS的新手。 - iJade
在控制器中,您需要确认转换。在您的情况下,它是 MyCtrl1MyCtrl2 控制器。 - Yair Nevet
在两个控制器中都需要更新。我已根据您的代码更新了我的问题,请看一下。 - iJade
你没有将 $scope 注入到你的控制器中! - Yair Nevet
@YairNevet,你让Firefox崩溃了!!!“e.preventDefault()”导致无限事件循环...请参考Scheintod以获取更健壮的版本。 - Exegesis

22

这是我使用的指令,当表单卸载时,它会自动清理自己。如果你想防止提示触发(例如因为你成功保存了表单),请调用$scope.FORMNAME.$setPristine(),其中FORMNAME是你想要防止提示的表单的名称。

.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();
                }
            });
        }
    };
}]);

2
我不得不使用$(window).bind('beforeunload', doPrompt);而不是window.addEventListener('beforeunload', areYouSurePrompt);(以及unbind用于removeEventListener)在FF 31.0中,因为本地的window调用不起作用(而window.onbeforeunload = function(){ return "sure?"; }却可以工作)?! 另外,从我所看到的情况来看,$locationChangeStart从未触发,考虑到使用onbeforeunload,这个块真的有必要吗? - Campbeln
@Campbeln 因为这个提示点了赞,上面的代码在 Chrome42 上也不起作用,我必须按照你所说的去做。 - Adriano Rosa

9
如上所述,您可以结合使用window.onbeforeunload$locationChangeStart来向用户发送消息。此外,您可以利用ngForm.$dirty仅在用户进行更改时向其发送消息。
我编写了一个angularjs指令,您可以将其应用于任何表单,它将自动监视更改并在重新加载页面或导航离开时向用户发送消息。请参见https://github.com/facultymatt/angular-unsavedChanges 希望您会发现这个指令有用!

2
其他在此处的示例适用于旧版本的ui-router (>=0.3.x),但是所有状态事件,例如$stateChangeStart,在1.0版之后被弃用。新的ui-router 1.0代码使用$transitions service。因此,您需要将$transitions注入到组件中,然后使用下面的代码演示的$transitions.onBefore方法。

$transitions.onBefore({}, function(transition) {
  return confirm("你确定要离开这个页面吗?");
});

这只是一个超级简单的示例。 $transitions服务可以接受更复杂的响应,例如承诺。有关详细信息,请参见HookResult类型


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

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

重新加载页面:使用init


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接