10

我是 Angular 的新手,我很难找到这个问题的根源。

我正在编写一个单页应用程序,并且正在处理身份验证部分。我有一个名为“sessionService”的服务,我希望能够在整个应用程序中使用它来确定用户是否登录。如果我这样做很简单:

...service('sessionService', function(...) { 
    /*...snip...*/
    this.isLoggedIn = function() { 
        return this.authenticated;
     };
});

“经过身份验证”的地方只是服务私有的。但是,如果我刷新页面,它就会崩溃。所以,我的想法是做这样的事情:

/*...snip...*/
this.isLoggedIn = function() { 
  var deferred = $q.defer()
    , self     = this
    ;

  function handleLoggedInStatus(status) {
    if (status) {
      self.authenticated = true;
      deferred.resolve();
    }
    else {
      deferred.reject();
    }
  }

  if (this.authenticated === null) {
    $http.get('/user')
      .success(function(response) {
        handleLoggedInStatus(response.success);
      });
  }
  else {
    handleLoggedInStatus(this.authenticated);
  }

  return deferred.promise;
};

然后在我的控制器中,我会做这样的事情:

$scope.isLoggedIn = sessionService.isLoggedIn;  

在我的模板中,我会这样做:

...data-ng-show="isLoggedIn()"

但是,这样做会导致以下错误:

10 $digest() iterations reached. Aborting!

我尝试了几种不同的方法来引用 sessionService.isLoggedIn 函数,例如:

$scope.isLoggedIn = sessionService.isLoggedIn();
$scope.isLoggedIn = sessionService.isLoggedIn.bind(sessionService)();
$scope.isLoggedIn = function() { return sessionService.isLoggedIn() }

但他们要么没有工作,要么只是给了我同样的错误。

基本上,我只是希望能够返回一个承诺,告诉我用户是否登录。如果我们不知道他们是否登录(比如在页面刷新之后),承诺将在之后解决一个ajax请求。如果我们已经知道(就像在整个单页应用程序中正常导航一样),那么承诺将立即得到解决。然后我想在我的视图中使用它,以便我可以显示/隐藏某些内容,例如注销或查看帐户页面的链接。

我究竟做错了什么?

4

1 回答 1

9

您正在解决您的承诺,但没有使用价值 - 所以$scope当解决时承诺的价值是undefined,这是虚假的,因此您ng-show没有触发。

看来您正在寻找更像这样的东西:

在服务中:

function handleLoggedInStatus(status) {
  if (status) {
    self.authenticated = true;
  }
  deferred.resolve(status); // always resolve, even if with falsy value
}

if (this.authenticated === null) {
  $http.get('/user')
    .success(function(response) {
      handleLoggedInStatus(response.success);
    })
    .error(function(data) {
      deferred.reject(data.errorMsg); // reject if there was an error
    });
} else {
  handleLoggedInStatus(this.authenticated);
}

在控制器中:

$scope.loggedIn = sessionService.isLoggedIn();

在 HTML 中:

<div ng-show='loggedIn'>...</div>

这是一个 JSFiddle,演示了使用真值解决延迟并绑定到$scope.


请注意,您不能将函数本身绑定到范围

$scope.loggedIn = sessionService.isLoggedIn

并在视图中调用函数

<div ng-show="loggedIn()">...</div>

因为该函数在每个摘要周期返回一个不同的承诺(这就是您收到“10 个摘要周期”错误的原因)。但是,您可以确保额外调用sessionService.isLoggedIn返回相同的Promise 而不是创建新的 Promise,因为您可以then多次调用 Promise(实际上这是 Promise 的好处之一):

deferred = null;

isLoggedIn: function() {
  if (!deferred) {
    deferred = $q.defer();
    $http.get('/user')
      .success(function(response) {
        deferred.resolve(response.success); // resolve if true or false
      })
      .error(function(data) {
        deferred.reject(data.errorMsg); // reject if there was an error
      });
  }
  return deferred.promise;
}

然后,您可以摆脱this.authenticated布尔值,因为您不需要跨函数调用跟踪先前登录的用户(因为 promise 会为您执行此操作)。

然而,虽然这消除了摘要循环错误,但您仍然无法从视图中调用该函数——我怀疑 Angular 将返回值(承诺本身)视为真实值,而不是绑定到承诺的已解决值。这是一个不起作用的例子;请注意,div即使承诺正在解决,也会显示false.


要用于deferred.reject指示用户经过身份验证,就像在您的原始服务中一样,您希望在控制器中做更多类似的事情,尽管我相信resolveing withfalse更干净:

sessionService.isLoggedIn()
  .then(function() {
    $scope.loggedIn = true; // resolved
  }).then(function() {
    $scope.loggedIn = false; // rejected
  });
于 2013-05-03T20:34:48.127 回答