AngularJs-ルート変更イベントをキャンセル


88

AngularJsでルート変更イベントをキャンセルするにはどうすればよいですか?

私の現在のコードは

$rootScope.$on("$routeChangeStart", function (event, next, current) {

// do some validation checks
if(validation checks fails){

    console.log("validation failed");

    window.history.back(); // Cancel Route Change and stay on current page  

}
});

これにより、検証が失敗した場合でも、Angularは次のテンプレートと関連データをプルし、すぐに前のビュー/ルートに戻ります。検証が失敗した場合、angularが次のテンプレートとデータをプルしたくありません。理想的には、window.history.back()がないはずです。私もevent.preventDefault()を試しましたが、役に立たなかった。

回答:


184

代わりに$routeChangeStart使用$locationChangeStart

これはangularjsの人たちからの議論です:https//github.com/angular/angular.js/issues/2109

2018年3月6日編集次のドキュメントで確認できます:https : //docs.angularjs.org/api/ng/service/$location#event-$locationChangeStart

例:

$scope.$on('$locationChangeStart', function(event, next, current) {
    if ($scope.form.$invalid) {
       event.preventDefault();
    }
});

8
これの問題は、ルートパラメータコレクションにアクセスする方法がないことです。ルートパラメータを検証しようとしている場合、このソリューションは適切ではありません。
KingOfHypocrites 2014

1
してくださいノートでは、使用している場合、変数は単なる文字列であり、それがどのようなデータを含めることはできません(たとえば、あなたが定義された以前にアクセスすることはできません変数を)$routeChangeStartnextauthorizedRoles
MyTitle

@KingOfHypocritesあなたはルート・パラメータを取得することはできませんが、あなたが得ることができる$location.path()$location.search()
エイドリアンによってデザインを

2
すべてのルート変更を追跡したい場合、rootScopeでこれを行うことをお勧めできますか?それとももっとおいしい代替品はありますか?
maxm

38

より完全なコードサンプル、使用 $locationChangeStart

// assuming you have a module called app, with a 
angular.module('app')
  .controller(
    'MyRootController',
    function($scope, $location, $rootScope, $log) {
      // your controller initialization here ...
      $rootScope.$on("$locationChangeStart", function(event, next, current) { 
        $log.info("location changing to:" + next); 
      });
    }
  );

私はこれを自分のルートコントローラー(トップレベルコントローラー)に接続することに完全に満足していません。より良いパターンがある場合は、知りたいです。私は角度が初めてです:-)


元のポスターのようにルート変更をキャンセルするつもりはありませんが、これは私にとってはうまくいきました。ありがとう!
Jim Clouse 14

4
rootScopeの問題は、コントローラーがなくなるときにハンドラーをアンバインドすることを覚えておかなければならないことです。
lostintranslation 2015

12

解決策は、「notAuthorized」イベントをブロードキャストし、それをメインスコープでキャッチして場所を再変更することです。それは最善の解決策ではないと思いますが、私にとってはうまくいきました:

myApp.run(['$rootScope', 'LoginService',
    function ($rootScope, LoginService) {
        $rootScope.$on('$routeChangeStart', function (event, next, current) {
            var authorizedRoles = next.data ? next.data.authorizedRoles : null;
            if (LoginService.isAuthenticated()) {
                if (!LoginService.isAuthorized(authorizedRoles)) {
                    $rootScope.$broadcast('notAuthorized');
                }
            }
        });
    }
]);

そして私のメインコントローラーで:

    $scope.$on('notAuthorized', function(){
        $location.path('/forbidden');
    });

注:この問題については、角度のあるサイトでいくつかの議論がありますが、まだ解決されていません:https : //github.com/angular/angular.js/pull/4192

編集:

コメントに答えるために、LoginServiceの動作に関する詳細を以下に示します。3つの機能が含まれています。

  1. login()(名前は誤解を招く)サーバーにリクエストを行い、(以前に)記録されたユーザーに関する情報を取得します。(SpringSecurityフレームワークを使用して)サーバーの現在のユーザー状態を入力するだけの別のログインページがあります。私のWebサービスは本当にステートレスではありませんが、有名なフレームワークに私のセキュリティを処理させることを好みました。
  2. isAuthenticated()は、クライアントセッションにデータが入力されているかどうかを検索します。つまり、以前に認証されたことを意味します(*)
  3. isAuthorized()がアクセス権を処理しました(このトピックの範囲外)。

(*)ルートが変更されると、マイセッションが入力されます。次に、when()メソッドをオーバーライドして、空のときにセッションを設定します。

ここにコードがあります:

services.factory('LoginService', ['$http', 'Session', '$q',
function($http, Session, $q){
    return {
        login: function () {
            var defer = $q.defer();
            $http({method: 'GET', url: restBaseUrl + '/currentUser'})
                .success(function (data) {
                    defer.resolve(data);
                });
            return defer.promise;
        },
        isAuthenticated: function () {
            return !!Session.userLogin;
        },
        isAuthorized: function (authorizedRoles) {
            if (!angular.isArray(authorizedRoles)) {
                authorizedRoles = [authorizedRoles];
            }

            return (this.isAuthenticated() &&  authorizedRoles.indexOf(Session.userRole) !== -1);
        }
    };
}]);

myApp.service('Session', ['$rootScope',
    this.create = function (userId,userLogin, userRole, userMail, userName, userLastName, userLanguage) {
        //User info
        this.userId = userId;
        this.userLogin = userLogin;
        this.userRole = userRole;
        this.userMail = userMail;
        this.userName = userName;
        this.userLastName = userLastName;
        this.userLanguage = userLanguage;
    };

    this.destroy = function () {
        this.userId = null;
        this.userLogin = null;
        this.userRole = null;
        this.userMail = null;
        this.userName = null;
        this.userLastName = null;
        this.userLanguage = null;
        sessionStorage.clear();
    };

    return this;
}]);

myApp.config(['$routeProvider', 'USER_ROLES', function ($routeProvider, USER_ROLES) {
    $routeProvider.accessWhen = function (path, route) {
        if (route.resolve == null) {
            route.resolve = {
                user: ['LoginService','Session',function (LoginService, Session) {
                    if (!LoginService.isAuthenticated())
                        return LoginService.login().then(function (data) {
                            Session.create(data.id, data.login, data.role, data.email, data.firstName, data.lastName, data.language);
                            return data;
                        });
                }]
            }
        } else {
            for (key in route.resolve) {
                var func = route.resolve[key];
                route.resolve[key] = ['LoginService','Session','$injector',function (LoginService, Session, $injector) {
                    if (!LoginService.isAuthenticated())
                        return LoginService.login().then(function (data) {
                            Session.create(data.id, data.login, data.role, data.email, data.firstName, data.lastName, data.language);
                            return func(Session, $injector);
                        });
                    else
                        return func(Session, $injector);
                }];
            }
        }
    return $routeProvider.when(path, route);
    };

    //use accessWhen instead of when
    $routeProvider.
        accessWhen('/home', {
            templateUrl: 'partials/dashboard.html',
            controller: 'DashboardCtrl',
            data: {authorizedRoles: [USER_ROLES.superAdmin, USER_ROLES.admin, USER_ROLES.system, USER_ROLES.user]},
            resolve: {nextEvents: function (Session, $injector) {
                $http = $injector.get('$http');
                return $http.get(actionBaseUrl + '/devices/nextEvents', {
                    params: {
                        userId: Session.userId, batch: {rows: 5, page: 1}
                    },
                    isArray: true}).then(function success(response) {
                    return response.data;
                });
            }
        }
    })
    ...
    .otherwise({
        redirectTo: '/home'
    });
}]);

LoginService.isAuthenticated()最初のページの読み込みで何が戻るか教えていただけますか?保管方法はcurrentUser?ユーザーがページを更新するとどうなりますか(ユーザーは資格情報を再入力する必要があります)?
MyTitle 2014年

LoginServiceに関する詳細情報を元の回答に追加しました。currentUserはサーバーによって提供され、ルート変更はページの更新を処理するため、ユーザーが再度ログインする必要はありません。
Asterius 14年

4

これにつまずく人は古い質問です(少なくとも角度1.4では)これを行うことができます:

 .run(function($rootScope, authenticationService) {
        $rootScope.$on('$routeChangeStart', function (event, next) {
            if (next.require == undefined) return

            var require = next.require
            var authorized = authenticationService.satisfy(require);

            if (!authorized) {
                $rootScope.error = "Not authorized!"
                event.preventDefault()
            }
        })
      })

6
中括弧や ";"の使用に追加料金はかかりますか?
2016

6
もちろん@MatthiasJansen。そして、すべてを締めくくるために、中括弧は二重に数え、セミコロンは三重に数えます。
AKOS Vandra

1

これは私の解決策であり、私にとってはうまくいきますが、私が正しい方法でWebテクノロジを初めて使用するのかどうかはわかりません。

var app = angular.module("app", ['ngRoute', 'ngCookies']);
app.run(function($rootScope, $location, $cookieStore){
$rootScope.$on('$routeChangeStart', function(event, route){
    if (route.mustBeLoggedOn && angular.isUndefined($cookieStore.get("user"))) {
        // reload the login route
        jError(
             'You must be logged on to visit this page',
             {
               autoHide : true,
               TimeShown : 3000,
               HorizontalPosition : 'right',
               VerticalPosition : 'top',
               onCompleted : function(){ 
               window.location = '#/signIn';
                 window.setTimeout(function(){

                 }, 3000)
             }
        });
    }
  });
});

app.config(function($routeProvider){
$routeProvider
    .when("/signIn",{
        controller: "SignInController",
        templateUrl: "partials/signIn.html",
        mustBeLoggedOn: false
});

2
答えがわからない場合、どうすれば質問に答えることができますか?
deW1 2014年

きっと動作するはずです。これが適切な方法かどうかはわかりません。あなたがより良い解決策を持っているなら、私はそれを見たいです。
Alexandrakis alexandros 2014年

1

私はこれが関連していると思いました

var myApp = angular.module('myApp', []);

myApp.run(function($rootScope) {
    $rootScope.$on("$locationChangeStart", function(event, next, current) { 
        // handle route changes  
$rootScope.error = "Not authorized!"
                event.preventDefault()   
    });
});

私の投稿は将来誰かを助けるかもしれません。


1
var app=angular
    .module('myapp', [])
    .controller('myctrl', function($rootScope) {
        $rootScope.$on("locationChangeStart", function(event, next, current) {
        if (!confirm("location changing to:" + next)) { 
            event.preventDefault();
        }
    })
});

2
このコードスニペットは問題を解決する可能性がありますが、説明を含めると、投稿の品質を向上させるのに役立ちます。あなたは将来の読者のための質問に答えていることを覚えておいてください、そしてそれらの人々はあなたのコード提案の理由を知らないかもしれません。
dpr

0

$routeChangeStartイベントでルートの変更を停止する必要がある場合(つまり、次のルートに基づいて何らかの操作を実行する場合)は、インジェクト$routeと内部$routeChangeStart呼び出しを行います。

$route.reload()

1
私は期待していましたが、ChromeのAngular 1.2.7では、これによりJSループが発生し、ページがフリーズします。
Nick Spacek 2014年

1
@NickSpacek呼び出す条件$route.reload()が異なる必要があります。そうでない場合、同じコードが再度実行されます。これは、条件としてを使用してwhileループを作成するのとtrue同じです。
Kevin Beal 2014年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.