ui-router 和登录 url return 状态

ui-router and login url return state

这是一个复杂的问题。 我正在尝试为用户尝试访问需要授权的页面时创建 returnState。

所以,我在状态 object 上创建了一些数据,如下所示:

$stateProvider.state('account', {
    url: '/account',
    templateUrl: '/app/account/index.tpl.html',
    controller: 'AccountController',
    controllerAs: 'controller',
    data: {
        requireLogin: true,
        state: 'account' // tells the page to redirect here if not logged in
    }
});

然后我在 stateChangeStart 上 运行 编写了这段代码:

.run(['$rootScope', '$state', 'AccountService', function ($rootScope, $state, account) {

    // On state change
    $rootScope.$on('$stateChangeStart', function (event, toState) {

        var data = toState.data; // Get our state data
        var requireLogin = typeof data === 'undefined' ? false : data.requireLogin; // Check to see if we have any data and if so, check to see if we need login rights
        var user = account.current() // Get our current user

        // If we require login rights and we are not authenticated
        if (requireLogin && !user.authenticated) {

            // Get our current state
            var state = data.state || '';

            // Stop processing
            event.preventDefault();

            // redirect to the login page and pass the state parameter
            $state.transitionTo('login', { returnState: state });
        }
    });
}]);

然后在我的 loginController 上我有这个功能:

self.loginUser = function () {

    // Login
    self.login = service.login(self.model);

    // If we login properly
    self.login.promise.then(function (response) {

        // If we have a return Url
        if (returnState) {

            // Go to that url
            $state.go(returnState);
        } else {

            // Otherwise, Redirect to the home page
            $state.go('home');
        }
    });
};

我展示的示例非常有效。但是现在我发现我有更复杂的需求。 我有另一组状态:

$stateProvider.state('designer', {
    url: '/:sport/designer',
    abstract: true,
    templateUrl: '/app/designer/designer.tpl.html',
    controller: 'DesignerController',
    controllerAs: 'controller'
}).state('designer.team', {
    url: '',
    templateUrl: '/app/designer/team.tpl.html'
}).state('designer.kit', {
    url: '/kit',
    templateUrl: '/app/designer/kit.tpl.html'
}).state('designer.design', {
    url: '/design',
    templateUrl: '/app/designer/design.tpl.html'
}).state('designer.refine', {
    url: '/refine',
    templateUrl: '/app/designer/refine.tpl.html'
}).state('designer.order', {
    url: '/order',
    templateUrl: '/app/designer/order.tpl.html'
}).state('designer.save', {
    url: '/save',
    templateUrl: '/app/designer/save.tpl.html',
    data: {
        requireLogin: true
    }
});

如您所见,这个状态和一些 children 以及最后一个状态(保存)要求用户登录。因为状态继承了它们的参数 parents 我应该是能够做类似的事情:

state: 'designer.save({ sport: :sport })'

但我收到一条错误消息:

Error: Could not resolve 'desginer.save({ sport: :sport })' from state 'login'

现在我假设这是因为 :sport 没有作为字符串传递,但我已经尝试过 alsorts 但我无法让它工作。 有没有更好的方法来处理路由?

我确实尝试过使用 stateChangeStart 函数来获取我们要移动的状态,但我似乎无法访问它。

首先,如果我们查看 the docs,我们可以看到 $state.go 接受以下参数:

  • 绝对状态名称
  • 参数
  • 选项

我没有看到任何地方提到将参数直接放入状态名称是合法的。所以我们有错误消息的原因。 'designer.save({ sport: :sport })' 不是 正确的绝对州名称。

Because states inherit the parameters of their parents

这意味着您不仅可以访问它们,而且在某些情况下它们会自动通过:

Any parameters that are not specified will be inherited from currently defined parameters. This allows, for example, going to a sibling state that shares parameters specified in a parent state. Parameter inheritance only works between common ancestor states, I.e. transitioning to a sibling will get you the parameters for all parents, transitioning to a child will get you all current parameters, etc.

因此,只要您处于 designer 状态或其任何子状态,您就不必提供任何参数。如果你不是,那么你需要找到一种方法将参数作为第二个参数传递给 $state.go.

感谢user2847643的建议。我实际上想出了一个更好的方法来处理这个问题。 首先,我将 login 状态修改为:

$stateProvider.state('login', {
    url: '/account/login',
    params: {
        returnState: '',
        returnParams: {}
    },
    views: {

        // the main template will be placed here (relatively named)
        '': {
            templateUrl: 'app/account/login/index.tpl.html'
        },

        // the child views will be defined here (absolutely named)
        'register@login': {
            templateUrl: 'app/account/login/register.tpl.html',
            controller: 'RegisterController',
            controllerAs: 'controller'
        },

        // for column two, we'll define a separate controller 
        'login@login': {
            templateUrl: 'app/account/login/login.tpl.html',
            controller: 'LoginController',
            controllerAs: 'controller'
        }
    }
});

如您所见,它接受两个可选参数。 然后我将登录功能修改为:

self.loginUser = function () {

    // Login
    self.login = service.login(self.model);

    // If we login properly
    self.login.promise.then(function (response) {

        console.log(returnState);
        console.log(returnParams);

        // If we have a return Url
        if (returnState) {

            // Go to that url
            $state.go(returnState, returnParams);
        } else {

            // Otherwise, Redirect to the home page
            $state.go('home');
        }
    });
};

现在可以正确调用传递状态和参数(或空对象)的 $state.go 函数。

我实际上将处理 returnStates 的逻辑放在我之前展示的 stateChangeStart 绑定中,所以现在看起来像这样:

$rootScope.$on('$stateChangeStart', function (event, toState, toParams) {

    var data = toState.data; // Get our state data
    var requireLogin = typeof data === 'undefined' ? false : data.requireLogin; // Check to see if we have any data and if so, check to see if we need login rights
    var user = account.current() // Get our current user

    // If we require login rights and we are not authenticated
    if (requireLogin && !user.authenticated) {

        // Get our current state
        var name = toState.name;
        var params = toParams;

        // If our state is the login page (i.e. we accessed it directly)
        if (name === 'login') {

            // Set our state to the homepage
            name = 'home';
            toParams = {};
        }

        // Stop processing
        event.preventDefault();

        // redirect to the login page and pass the state parameter
        $state.transitionTo('login', { returnState: name, returnParams: params });
    }
});

我们已经做到了。

感谢 r3plica post。我遇到的问题很少,并通过以下方式解决了。如果我错了,有人可以纠正我。

  1. 你应该使用ui-router最新版本,旧版本不支持params。我正在使用 0.2.13.
  2. 在控制器中,您需要读取参数为 $state.go($stateParams.returnState, $stateParams.returnParams)
  3. 在$stateChangeStart事件中,还需要传递参数fromState,fromParams。这是用来检查fromState是否不是登录,所以你一次又一次地跳过重新加载登录页面。

    $rootScope.$on('$stateChangeStart', function(event, toState, toParams, fromState, fromParams){ // var restrictedPage = $.inArray($location.path(),['/landing', '/list','/landing/users']);

        var data = toState.data;
        if(typeof toState.data !== 'undefined' && fromState.name !== 'login'){......}
       }