我正在写一个小的AngularJS应用程序,它有一个登录视图和一个主视图,配置如下:

$routeProvider
 .when('/main' , {templateUrl: 'partials/main.html',  controller: MainController})
 .when('/login', {templateUrl: 'partials/login.html', controller: LoginController})
 .otherwise({redirectTo: '/login'});

我的LoginController检查user/pass组合,并在$rootScope上设置一个属性:

function LoginController($scope, $location, $rootScope) {
 $scope.attemptLogin = function() {
   if ( $scope.username == $scope.password ) { // test
        $rootScope.loggedUser = $scope.username;
        $location.path( "/main" );
    } else {
        $scope.loginError = "Invalid user/pass.";
    }
}

一切正常,但如果我访问http://localhost/#/main,我最终绕过了登录屏幕。我想写一些类似于“每当路由改变,如果$rootScope。loggedUser为空,然后重定向到/login"

...

... 等待。我能以某种方式听到路线变化吗?不管怎样,我会把这个问题发布出来,继续寻找。


当前回答

在深入研究了一些文档和源代码之后,我想我已经让它工作了。也许这对其他人有用?

我在我的模块配置中添加了以下内容:

angular.module(...)
 .config( ['$routeProvider', function($routeProvider) {...}] )
 .run( function($rootScope, $location) {

    // register listener to watch route changes
    $rootScope.$on( "$routeChangeStart", function(event, next, current) {
      if ( $rootScope.loggedUser == null ) {
        // no logged user, we should be going to #login
        if ( next.templateUrl != "partials/login.html" ) {
          // not going to #login, we should redirect now
          $location.path( "/login" );
        }
      }         
    });
 })

有一件事似乎很奇怪,我必须测试部分名称(login.html),因为“next”Route对象没有url或其他东西。也许有更好的办法?

其他回答

    $routeProvider
 .when('/main' , {templateUrl: 'partials/main.html',  controller: MainController})
 .when('/login', {templateUrl: 'partials/login.html', controller: LoginController}).
 .when('/login', {templateUrl: 'partials/index.html', controller: IndexController})
 .otherwise({redirectTo: '/index'});

实现登录重定向的另一种方法是使用这里描述的事件和拦截器。本文描述了一些额外的优点,例如检测何时需要登录、排队请求以及在登录成功后重新播放它们。

您可以在这里尝试一个工作演示,并在这里查看演示源代码。

如果你不想使用angular-ui-router,但想让你的控制器通过RequireJS惰性加载,当你使用你的控制器作为RequireJS模块(惰性加载)时,事件$routeChangeStart会有几个问题。

你不能确定控制器会在$ routechangstart被触发之前被加载——事实上它不会被加载。这意味着您不能访问下一个路由的属性,如locals或$$route,因为它们还没有设置。 例子:

app.config(["$routeProvider", function($routeProvider) {
    $routeProvider.when("/foo", {
        controller: "Foo",
        resolve: {
            controller: ["$q", function($q) {
                var deferred = $q.defer();
                require(["path/to/controller/Foo"], function(Foo) {
                    // now controller is loaded
                    deferred.resolve();
                });
                return deferred.promise;
            }]
        }
    });
}]);

app.run(["$rootScope", function($rootScope) {
    $rootScope.$on("$routeChangeStart", function(event, next, current) {
        console.log(next.$$route, next.locals); // undefined, undefined
    });
}]);

这意味着您不能检查其中的访问权限。

解决方案:

由于控制器的加载是通过resolve完成的,你可以对你的访问控制检查做同样的事情:

app.config(["$routeProvider", function($routeProvider) {
    $routeProvider.when("/foo", {
        controller: "Foo",
        resolve: {
            controller: ["$q", function($q) {
                var deferred = $q.defer();
                require(["path/to/controller/Foo"], function(Foo) {
                    // now controller is loaded
                    deferred.resolve();
                });
                return deferred.promise;
            }],
            access: ["$q", function($q) {
                var deferred = $q.defer();
                if (/* some logic to determine access is granted */) {
                    deferred.resolve();
                } else {
                    deferred.reject("You have no access rights to go there");
                }
                return deferred.promise;
            }],
        }
    });
}]);

app.run(["$rootScope", function($rootScope) {
    $rootScope.$on("$routeChangeError", function(event, next, current, error) {
        console.log("Error: " + error); // "Error: You have no access rights to go there"
    });
}]);

注意,这里不是使用事件$routeChangeStart,而是使用$routeChangeError

以下是我的做法,希望对大家有所帮助:

在配置中,我在我想向公众开放的少数路由上设置了一个publicAccess属性(比如登录或注册):

$routeProvider
    .when('/', {
        templateUrl: 'views/home.html',
        controller: 'HomeCtrl'
    })
    .when('/login', {
        templateUrl: 'views/login.html',
        controller: 'LoginCtrl',
        publicAccess: true
    })

然后在一个运行块中,我在$routeChangeStart事件上设置了一个监听器,它重定向到'/login',除非用户有访问权限或路由是公共可访问的:

angular.module('myModule').run(function($rootScope, $location, user, $route) {

    var routesOpenToPublic = [];
    angular.forEach($route.routes, function(route, path) {
        // push route onto routesOpenToPublic if it has a truthy publicAccess value
        route.publicAccess && (routesOpenToPublic.push(path));
    });

    $rootScope.$on('$routeChangeStart', function(event, nextLoc, currentLoc) {
        var closedToPublic = (-1 === routesOpenToPublic.indexOf($location.path()));
        if(closedToPublic && !user.isLoggedIn()) {
            $location.path('/login');
        }
    });
})

显然,你可以将条件从isLoggedIn更改为其他任何条件…只是展示了另一种方法。

我也一直在努力这么做。在和同事一起工作后,我想出了另一个更简单的解决方案。我在$location.path()上设置了一个手表。这很管用。我刚开始学习AngularJS,发现这是更干净和可读的。

$scope.$watch(function() { return $location.path(); }, function(newValue, oldValue){  
    if ($scope.loggedIn == false && newValue != '/login'){  
            $location.path('/login');  
    }  
});