【问题标题】:Angular infinite digest loop with ui-router带有 ui-router 的 Angular 无限摘要循环
【发布时间】:2015-01-30 14:51:32
【问题描述】:

我最初试图解决的问题是,如果用户未登录,则将其重定向到登录页面,反之亦然。

我用下面的代码做到了这一点

.run(function($rootScope, $http, AppService, $state) {
    $rootScope.$on('application:refreshtoken', function(rootScope, token) {
        if(token) {
            $http.defaults.headers.common['X-Auth-Token'] = token;
            AppService.setAuthToken(token);
            AppService.resetLoginTimeout();
        }
    });
    $rootScope.$on('$stateChangeSuccess', function() {
        $http.get('/api/heartbeat');
    });

    // This is the really pertinent bit...
    $rootScope.$on('$stateChangeStart', function(e, toState) {
        if(toState.name === 'login') {
            if(AppService.getIsLoggedIn()) {
                e.preventDefault();
                $state.go(AppService.getRedirectPage());
            }
        } else {
            if(!AppService.getIsLoggedIn()) {
                e.preventDefault();
                $state.go('login');
            }
        }
    });
 });

应用服务

.factory('AppService', ['$rootScope', 'locker', '$http', '$state',
  function ($rootScope, locker, $http, $state) {

    var _isLoggedIn = locker.get('loggedIn', false),
      _authToken = locker.get('authtoken', null),
      _roles = locker.get('roles', null),
      _permissions = locker.get('permissions', null),
      _user = locker.get('user', null),
      _userid = locker.get('userid', null),
      _userprefs = locker.get('userprefs', null),
      _timedout,
      _timeoutId,
      service = {};

    if (_authToken) {
      $http.defaults.headers.common['X-Auth-Token'] = _authToken;
    }

    service.setIsLoggedIn = function (isLoggedIn) {
      _isLoggedIn = isLoggedIn;
      this.doLogin();
      broadcastLogin();
    };

    service.doLogin = function () {
      if (_isLoggedIn) {
        locker.put({
          loggedIn: _isLoggedIn,
          authtoken: _authToken,
          roles: _roles,
          permissions: _permissions,
          user: _user,
          userprefs: _userprefs
        });
      }
    };

    service.doLogout = function (cb) {
      _isLoggedIn = false;
      _authToken = null;
      _roles = null;
      _permissions = null;
      _user = null;
      _userid = null;
      _userprefs = null;

      delete $http.defaults.headers.common['X-Auth-Token'];

      locker.clean();

      cb();
    };


    service.getIsLoggedIn = function () {
      return _isLoggedIn;
    };

    service.setAuthToken = function (authToken) {
      _authToken = authToken;
      locker.put({
        authtoken: _authToken
      });

    };

    service.getAuthToken = function () {
      return _authToken;
    };

    service.setUserid = function (userid) {
      locker.put('userid', userid);
      _userid = userid;
    };

    service.getUserid = function () {
      return _userid;
    };

    service.setUser = function (user) {
      _user = user;
    };

    service.getUser = function () {
      return _user;
    };

    service.setRoles = function (roles) {
      _roles = roles;
    };

    service.getRoles = function () {
      return _roles;
    };

    service.setPermissions = function (permissions) {
      _permissions = permissions;
    };

    service.getPermissions = function () {
      return _permissions;
    };

    service.setUserPreferences = function (prefs) {
      _userprefs = prefs;
    };

    service.getUserPreferences = function () {
      return _userprefs;
    };

    service.resetLoginTimeout = function () {
      if (_timeoutId) {
        clearTimeout(_timeoutId);
      }
      _timeoutId = setTimeout(function () {
        $rootScope.$broadcast('application:logintimeoutwarn');
      }, 1000 * 60 * 4);
    };

    service.setTimedOut = function (timedout) {
      _timedout = timedout;
    };

    service.getTimedOut = function () {
      return _timedout;
    };

    service.extendSession = function () {
      $http.get('/api/heartbeat');
    };

    service.goDefaultUserPage = function () {
      var success = false;
      if (_userprefs.landingPage) {
        $state.go(_userprefs.landingPage);
        success = true;
      } else {
        var permissionRoutes = {
          'regimens': 'regimens.do',
          'pathways': 'pathways',
          'manage.users': 'manageusers.do',
          'manage.practices': 'managepractices.do',
          'patients': 'patients'
        };
        _.some(_permissions, function (el) {
          var state = $state.get(permissionRoutes[el]);
          if (!state.abstract) {
            $state.go(state.name);
            success = true;
            return true;
          }
        });
      }
      return success;
    };

    service.getRedirectPage = function () {
      var page = false;
      if (_userprefs.landingPage) {
        page = _userprefs.landingPage;
      } else {
        var permissionRoutes = {
          'regimens': 'regimens.do',
          'pathways': 'pathways',
          'manage.users': 'manageusers.do',
          'manage.practices': 'managepractices.do',
          'patients': 'patients'
        };
        _.some(_permissions, function (el) {
          var state = $state.get(permissionRoutes[el]);
          if (!state.abstract) {
            page = state.name;
            return true;
          }
        });
      }
      return page;
    };

    function broadcastLogin() {
      $rootScope.$broadcast('application:loggedinstatus');
    }

    broadcastLogin();

    return service;

  }
])

在我采取一组非常具体的操作之前,这段代码运行良好:

  1. 登录
  2. 关闭打开的标签或窗口
  3. 打开一个新标签并转到应用程序

由于我仍然登录到应用程序,我有一个用户对象和一个有效的令牌,但我得到了error:infdig Infinite $digest Loop。它最终会解决并进入正确的状态,但需要一段时间并且路径会闪烁(如果需要,我可以发布视频)。

我尝试在$rootScope.$on('$stateChangeSuccess') 回调中使用$location.path 而不是$state.go,但问题仍然存在。

这并不会真正影响应用程序的运行,但会很烦人。我也不想将我的储物柜存储更改为会话存储,因为我希望用户在关闭选项卡并重新打开时保持登录状态。

【问题讨论】:

    标签: angularjs angular-ui-router


    【解决方案1】:

    我想说,问题隐藏在 $rootScope.$on('$stateChangeStart'... 内不正确的 if 语句中检查:

    一般性建议:

    让我们重定向 ($state.go()) 仅在需要时 - 否则退出事件监听器

    $rootScope.$on('$stateChangeStart' ...
      if (toState.name === 'login' ){
        // going to login ... do not solve it at all
        return;
      }
    

    第二个检查应该是:用户是否经过身份验证(并且不会登录)

    if(AppService.getIsLoggedIn()) {
      // do not redirect, let him go... he is AUTHENTICATED
      return;
    }
    

    现在我们有了状态,不是登录,用户没有通过身份验证,我们可以清楚地调用:

    // this is a must - stop current flow
    e.preventDefault();
    $state.go('login'); // go to login
    

    一切都会像我们预期的那样工作

    很详细的解释和working example could be also found here...

    【讨论】:

    • 好的,但如果他们已经登录,我不希望他们进入登录页面,我是否应该向 LoginCtrl 添加逻辑,如果他们已经登录则重定向他们并将其退出$stateChangeStart 听众?
    • 我会说不。请详细检查我的答案。我说:如果用户已经导航到其他路线......并且是AppService.getIsLoggedIn() - 让他进入那个状态。仅以防万一,他没有经过身份验证并且他没有登录......他应该被重定向。你懂我的意思吗?现在有用吗?这种方式我使用它很长时间并且它正在工作......只需在用户登录后......你应该调用 $state.go("anywhere") 并且我的答案中的解决方案不会停止它;)
    【解决方案2】:

    这通常发生在应用程序在通过 resolve 子句的路由拒绝和上一个路由上的自动重定向之间卡住时发生如果它失败或者它会重定向回其他页面,因此循环,请确保你的故事直截了当,如果需要,使用中间状态来清除所有首选项并采用默认路径

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-07-09
      • 2014-04-19
      相关资源
      最近更新 更多