$ stateChangeStart从REST API获取数据之前运行 [英] $stateChangeStart run before getting data from Rest API

查看:782
本文介绍了$ stateChangeStart从REST API获取数据之前运行的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试着做用户登录验证,我做了一个名为服务 AuthService 用的方法 isAuthenticated 中,我打个电话给API来交叉检查用户登录或没有(另外我会在用户在以后的用户角色),然后我称之为 $ rootScope此服务。$在,但它不是等到我的服务获得API的数据,这是我的app.js

  VAR应用= angular.module('对myApp',['ngRoute','ui.router','ngAnimate,烤面包机,ui.bootstrap']);的app.config(['$ routeProvider','$ locationProvider','$ stateProvider','$ urlRouterProvider',
  功能($ routeProvider,$ locationProvider,$ stateProvider,$ urlRouterProvider){
        $ stateProvider。
        状态('/',{
            网址:/,
            观点:{
                标题:{
                    templateUrl:'谐音/普通/ header.html中',
                    控制器:'authCtrl',
                },
                内容:{
                    templateUrl:'谐音/ dashboard.html',
                    控制器:'authCtrl',
                }
            },
            标题:仪表板,
            验证:真
        })        .STATE('登陆',{
            网址:/登录
            观点:{
                内容:{
                    templateUrl:'谐音/ login.html的,
                    控制器:'authCtrl',
                }
            },
            标题:登录,
            验证:假的
        })        .STATE(仪表盘,{
            标题:仪表板,
            网址:/仪表板,
            观点:{
                标题:{
                    templateUrl:'谐音/普通/ header.html中',
                    控制器:'authCtrl',
                },
                内容:{
                    templateUrl:'谐音/ dashboard.html',
                    控制器:'authCtrl',
                }
            },
            验证:真
        });
        $ urlRouterProvider.otherwise(/);        //检查浏览器支持
        如果(的window.history&安培;&安培; window.history.pushState){
            $ locationProvider.html5Mode({
                 启用:真实,
                 requireBase:假的
            });
        }
  }])
 .RUN(函数($ rootScope,$位置$状态数据,AuthService){
        $ rootScope。在$($ stateChangeStart功能(事件,toState,toParams,fromState,fromParams){
            $ rootScope.title = toState.title;
            $ rootScope.authenticated = FALSE;
            VAR USERINFO = AuthService.isAuthenticated();
            如果(toState.authenticate&放大器;&放大器;!AuthService.isAuthenticated()){
                  //用户没有通过验证
                  $ state.transitionTo(登录);
                  。事件preventDefault();
            }其他{
               警报('我在这里='+ $ rootScope.authenticated);
            }
        });
    });

下面是我的服务

  app.factory('AuthService',函数($ HTTP,数据,$ rootScope){
  变种authService = {};  authService.isAuthenticated =功能(){
    Data.get('的index.php / API /认证/ is_login'),然后(功能(结果){
        如果(results.uid){
            $ rootScope.authenticated =真;
            $ rootScope.uid = results.uid;
            $ rootScope.name = results.name;
            $ rootScope.email = results.email;
            返回results.uid;
        }其他{
            返回false;
        }
    });
  }
  返回authService;
});

试过很多东西,但至今没有运气,请建议做一个最好的方法,我试过在UI的路线的决心方法,但没有奏效。

先谢谢了。


解决方案

这是您目前将无法工作,因为 Data.get()函数返回的实施<因此code>无极,并且是异步的。正因为如此,该航线变化将继续,而不必等待,直到你的身份验证服务返回的任何值。

要解决您的问题,您应该以特定方式处理这种异步逻辑:

  app.run(函数($ rootScope,$位置$状态数据,AuthService){
  $ rootScope。在$('$ stateChangeStart',函数(事件,toState,toParams,fromState,fromParams){
    / **
     *如果用户正在走向一个需要国家标题
     *认证,假设他们没有通过身份验证,
     *阻止他们!否则,让他们通过...
     * /
    如果(toState.authenticate){
      。事件preventDefault();      如果(toState.name!== fromState.name){
        //现在检查它们是否被验证
        AuthService.isAuthenticated()。然后(函数(){
          //没错!他们验证,继续
          $ state.go(toState.name,toParams);
        },函数(){
          //不,该用户不能查看此页
          警报('你不能查看此页!');
        });
      }
    }其他{
      返回;
    }
  });
}


  

编辑:我添加了一个检查语句 toState.name == fromState.name 来打破任何重定向循环,可能进而导致不已!调用身份验证服务。这是在回应这个问题的楼主发表了意见。



  

诀窍是prevent路线过渡,的默认的。如你希望允许用户将任何进一步之前,你就能够完成尽可能多的异步逻辑。


  
  

我会建议增加一个微调,或者加载窗口,提醒用户到要执行此认证检查的事实。这也可能是明智的prevent页面上的任何进一步的用户操作,但是这完全取决于你。


请注意,该函数 AuthService.isAuthenticated()现在返回无极,你将不得不稍作改变你的身份验证服务来实现这一目标。简单地包装你目前的逻辑是这样的:

  app.factory('AuthService',函数($ HTTP,数据,$ rootScope,$ Q){
  变种authService = {};  authService.isAuthenticated =功能(){
    返回$ Q(函数(解析,拒绝){
      Data.get('的index.php / API /认证/ is_login'),然后(功能(结果){
        如果(results.uid){
          $ rootScope.authenticated =真;
          $ rootScope.uid = results.uid;
          $ rootScope.name = results.name;
          $ rootScope.email = results.email;          //大,用户通过验证,解决了无极
          解决(results.uid);
        }其他{
          //用户似乎并没有进行身份验证,拒绝承诺
          拒绝();
        }
      });
    });
  };  返回authService;
});

我希望这个解决您的问题,承诺可以是一个痛苦,但他们也可以是pretty有力工具。有在GitHub上一个很好的线程讨论了一些解决此问题的潜在解决方案,可以在这里找到的:的 https://github.com/angular-ui/ui-router/issues/1399


I'm tried to do user login authentication, I've made a service named AuthService with a method isAuthenticated in which I make a call to API to cross check the user is logged in or not (Also I'll user the user role in it later), and then I call this service in $rootScope.$on but its not wait until my service get data from API, Here is my app.js

var app = angular.module('myApp', ['ngRoute', 'ui.router', 'ngAnimate', 'toaster', 'ui.bootstrap']);

app.config(['$routeProvider', '$locationProvider', '$stateProvider', '$urlRouterProvider',
  function ($routeProvider, $locationProvider, $stateProvider, $urlRouterProvider) {
        $stateProvider.
        state('/', {
            url: "/",
            views: {
                header: {
                    templateUrl: 'partials/common/header.html',
                    controller: 'authCtrl',
                },
                content: {
                    templateUrl: 'partials/dashboard.html',
                    controller: 'authCtrl',
                }
            },
            title: 'Dashboard',
            authenticate: true
        })

        .state('login', {
            url: "/login",
            views: {
                content: {
                    templateUrl: 'partials/login.html',
                    controller: 'authCtrl',
                }
            },
            title: 'Login',
            authenticate: false
        })        

        .state('dashboard', {
            title: 'Dashboard',
            url: "/dashboard",
            views: {
                header: {
                    templateUrl: 'partials/common/header.html',
                    controller: 'authCtrl',
                },
                content: {
                    templateUrl: 'partials/dashboard.html',
                    controller: 'authCtrl',
                }
            },
            authenticate: true
        });
        $urlRouterProvider.otherwise("/");

        //check browser support
        if(window.history && window.history.pushState){
            $locationProvider.html5Mode({
                 enabled: true,
                 requireBase: false
            });
        }
  }])
 .run(function ($rootScope, $location, $state, Data, AuthService) {
        $rootScope.$on("$stateChangeStart", function (event, toState, toParams, fromState, fromParams) {
            $rootScope.title = toState.title;
            $rootScope.authenticated = false;
            var userInfo = AuthService.isAuthenticated();
            if (toState.authenticate && !AuthService.isAuthenticated()){
                  // User isn’t authenticated
                  $state.transitionTo("login");
                  event.preventDefault();
            } else {
               alert('here I am ='+$rootScope.authenticated);
            }
        });
    });

Here is my service

app.factory('AuthService', function ($http, Data, $rootScope) {
  var authService = {};

  authService.isAuthenticated = function () {
    Data.get('index.php/api/authentication/is_login').then(function (results) {
        if (results.uid) {
            $rootScope.authenticated = true;
            $rootScope.uid = results.uid;
            $rootScope.name = results.name;
            $rootScope.email = results.email;
            return results.uid;
        } else {
            return false;
        }
    });
  }
  return authService;
});

Tried lot of things but no luck so far, Please suggest a best way to do it, I've tried the resolve method in ui-routes but didn't work.

Thanks in advance.

解决方案

The implementation that you currently have will not work because the Data.get() function returns a Promise, and is therefore asynchronous. Because of this, the route change will go ahead without waiting until your authentication service has returned any value.

To resolve your issue, you should handle this asynchronous logic in a particular way:

app.run(function ($rootScope, $location, $state, Data, AuthService) {
  $rootScope.$on('$stateChangeStart', function (event, toState, toParams, fromState, fromParams) {
    /**
     * If the user is heading towards a state that requires
     * authentication, assume that they are not authenticated,
     * and stop them! Otherwise, let them through...
     */
    if (toState.authenticate) {
      event.preventDefault();

      if (toState.name !== fromState.name) {
        // Now check whether or not they are authenticated
        AuthService.isAuthenticated().then(function () {
          // Yes! They're authenticated, continue
          $state.go(toState.name, toParams);
        }, function () {
          // Nope, this user cannot view this page
          alert('You cannot view this page!');
        });
      }
    } else {
      return;
    }
  });
}

Edit: I have added a check statement toState.name !== fromState.name to break any redirect loops that may then cause endless calls to the authentication service. This was in response to a comment made by the original poster of the issue.

The trick is to prevent the route transition, by default. You are then able to perform as much asynchronous logic as you wish before the user is permitted to move any further on.

I would recommend adding a spinner, or a loading window, to alert the user to the fact that you are performing this authentication check. It may also be wise to prevent any further user actions on the page, but that's entirely up to you.

Note that the function AuthService.isAuthenticated() now returns a Promise, you will have to make a minor change to your authentication service to achieve this. Simply wrap your current logic like this:

app.factory('AuthService', function ($http, Data, $rootScope, $q) {
  var authService = {};

  authService.isAuthenticated = function () {
    return $q(function (resolve, reject) {
      Data.get('index.php/api/authentication/is_login').then(function (results) {
        if (results.uid) {
          $rootScope.authenticated = true;
          $rootScope.uid = results.uid;
          $rootScope.name = results.name;
          $rootScope.email = results.email;

          // Great, the user is authenticated, resolve the Promise
          resolve(results.uid);
        } else {
          // The user does not appear to be authenticated, reject the Promise
          reject();
        }
      });
    });
  };

  return authService;
});

I hope that this resolves your issue, Promises can be a pain, but they can also be a pretty powerful tool. There was a good thread on GitHub discussing a number of potential solutions around this issue that can be found here: https://github.com/angular-ui/ui-router/issues/1399.


这篇关于$ stateChangeStart从REST API获取数据之前运行的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆