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-08-30

1
변수를 사용 $routeChangeStart하는 경우 next단지 문자열이고 데이터를 포함 할 수 없습니다 (예 : 이전에 정의 된 authorizedRoles변수에 액세스 할 수 없음 )
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 2014

4
예, rootScope의 문제는 컨트롤러가 사라질 때 해당 핸들러의 바인딩을 해제해야한다는 것입니다.
lostintranslation

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 프레임 워크 사용). 내 웹 서비스는 상태 비 저장이 아니지만 유명한 프레임 워크가 내 보안을 처리하도록하는 것을 선호했습니다.
  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 2014

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
중괄호 또는 ";"사용에 대해 추가 요금이 부과됩니까?
cel sharp

6
물론 @MatthiasJansen. 그리고 모든 것을 마무리하기 위해 중괄호는 두 배로 계산하고 세미콜론은 세 배로 계산합니다.
Ákos Vandra

1

이것은 내 솔루션이며 나를 위해 작동하지만 웹 기술을 처음 접하기 때문에 올바른 길을 가고 있는지 모르겠습니다.

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 년

0

제 경우 에는 $ routeChangeStart로 경로 확인을 연기 하고 싶습니다 . 경로 확인이 시작되기 전에로드해야하는 SomethingService (예, 수다스러운 애플리케이션)가 있으므로 기다릴 약속이 있습니다. 해킹을 찾았을 수도 있습니다 ... 해결이 거부를 반환하면 경로의 해결에 오류가 발생합니다. 해결 구성을 깨고 나중에 다시 수정합니다.

    var rejectingResolve = {
        cancel: function ($q){
            // this will cancel $routeChangeStart
            return $q.reject();
        }
    }
    
    $rootScope.$on("$routeChangeStart", function(event, args, otherArgs) {
        var route = args.$$route,
            originalResolve = route.resolve;
    
        if ( ! SomethingService.isLoaded() ){

            SomethingService.load().then(function(){
                // fix previously destroyed route configuration
                route.resolve = originalResolve;
                
                $location.search("ts", new Date().getTime());
                // for redirections
                $location.replace();
            });

            // This doesn't work with $routeChangeStart: 
            // we need the following hack
            event.preventDefault();
            
            // This is an hack! 
            // We destroy route configuration, 
            // we fix it back when SomethingService.isLoaded
            route.resolve = rejectingResolve;
        } 
    });
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.