Angular - 绑定到返回promise的函数

时间:2021-08-04 19:55:23

I am new to angular, and I am having a tough time getting to the bottom of this problem.

我是棱角分明的新手,我很难找到问题的根源。

I am writing a single-page application, and am working on the authentication portion. I have a service called "sessionService" that I want to be able to use throughout the app to determine if the user is logged in or not. It is simple if I do something like this:

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

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

Where "authenticated" is just private to the service. However, the falls apart if I refresh the page. So, my thought was to do something like this:

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

/*...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;
};

And then in my controller I would do something like this:

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

$scope.isLoggedIn = sessionService.isLoggedIn;  

And in my template I would do:

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

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

However, doing that would result in the following error:

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

10 $digest() iterations reached. Aborting!

I tried a few different ways of referencing the sessionService.isLoggedIn function, such as:

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

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

But they either didn't work, or just gave me the same error.

但它们要么不起作用,要么只是给了我同样的错误。

Basically, I just want to be able to return a promise that will tell me whether or not the user is logged in. If we don't know if they are logged in (like after a page refresh), the promise will be resolved after an ajax request. If we do know already (like with normal navigation throughout the single page app) then the promise will be resolved immediately. I would then like to use that in my views so I can show/hide certain things, such as links to logout or view the account page.

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

What am I doing wrong?

我究竟做错了什么?

1 个解决方案

#1


9  

You're resolving your promise, but not with a value--so the value of the promise on the $scope when resolved is undefined, which is falsy, thus your ng-show is not triggering.

您正在解决您的承诺,但没有使用值 - 因此,解析时$ scope上的promise的值是未定义的,这是假的,因此您的ng-show不会触发。

It seems you're looking for something more like this:

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

In the service:

在服务中:

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);
}

In the controller:

在控制器中:

$scope.loggedIn = sessionService.isLoggedIn();

In the HTML:

在HTML中:

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

Here is a JSFiddle demonstrating resolving the deferred with a truthy value and binding to the $scope.

这是一个JSFiddle,演示了使用truthy值解析延迟并绑定到$ scope。


Note that you can't bind the function itself to the scope

请注意,您无法将函数本身绑定到范围

$scope.loggedIn = sessionService.isLoggedIn

and call the function in the view

并在视图中调用该函数

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

because the function returns a different promise each digest cycle (which is why you were getting the '10 digest cycles' error). You could, however, ensure that extra calls to sessionService.isLoggedIn returns the same promise instead of creating a new one, since you can call then on a promise multiple times (and in fact this is one of the benefits of promises):

因为该函数每个摘要周期返回一个不同的promise(这就是为什么你得到'10摘要周期'错误)。但是,您可以确保对sessionService.isLoggedIn的额外调用返回相同的promise而不是创建新的promise,因为您可以多次调用promise(事实上这是promises的好处之一):

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;
}

You could then get rid of the this.authenticated boolean, as you do not need to keep track of a previously-logged-in user across function calls (since the promise does this for you).

然后你可以摆脱this.authenticated布尔值,因为你不需要跟踪函数调用之前登录的用户(因为promise会为你做这个)。

However, while this gets rid of the digest cycle error, you still cannot call the function from the view--I suspect Angular is treating the return value (the promise itself) as a truthy value, rather than binding to the promise's resolved value. Here's an example of it not working; notice the div is displayed even though the promise is resolving with false.

然而,虽然这消除了摘要周期错误,但您仍然无法从视图中调用该函数 - 我怀疑Angular将返回值(promise本身)视为一个truthy值,而不是绑定到promise的已解析值。这是一个不起作用的例子;请注意,即使promise以false结算,也会显示div。


To use deferred.reject to indicate the user was not authenticated, as in your original service, you'd want to do something more like this in the controller, though I believe that resolveing with false is cleaner:

要使用deferred.reject来指示用户未经过身份验证,就像在原始服务中一样,您希望在控制器中执行更多类似的操作,但我相信使用false进行解析更清晰:

sessionService.isLoggedIn()
  .then(function() {
    $scope.loggedIn = true; // resolved
  }).then(function() {
    $scope.loggedIn = false; // rejected
  });

#1


9  

You're resolving your promise, but not with a value--so the value of the promise on the $scope when resolved is undefined, which is falsy, thus your ng-show is not triggering.

您正在解决您的承诺,但没有使用值 - 因此,解析时$ scope上的promise的值是未定义的,这是假的,因此您的ng-show不会触发。

It seems you're looking for something more like this:

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

In the service:

在服务中:

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);
}

In the controller:

在控制器中:

$scope.loggedIn = sessionService.isLoggedIn();

In the HTML:

在HTML中:

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

Here is a JSFiddle demonstrating resolving the deferred with a truthy value and binding to the $scope.

这是一个JSFiddle,演示了使用truthy值解析延迟并绑定到$ scope。


Note that you can't bind the function itself to the scope

请注意,您无法将函数本身绑定到范围

$scope.loggedIn = sessionService.isLoggedIn

and call the function in the view

并在视图中调用该函数

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

because the function returns a different promise each digest cycle (which is why you were getting the '10 digest cycles' error). You could, however, ensure that extra calls to sessionService.isLoggedIn returns the same promise instead of creating a new one, since you can call then on a promise multiple times (and in fact this is one of the benefits of promises):

因为该函数每个摘要周期返回一个不同的promise(这就是为什么你得到'10摘要周期'错误)。但是,您可以确保对sessionService.isLoggedIn的额外调用返回相同的promise而不是创建新的promise,因为您可以多次调用promise(事实上这是promises的好处之一):

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;
}

You could then get rid of the this.authenticated boolean, as you do not need to keep track of a previously-logged-in user across function calls (since the promise does this for you).

然后你可以摆脱this.authenticated布尔值,因为你不需要跟踪函数调用之前登录的用户(因为promise会为你做这个)。

However, while this gets rid of the digest cycle error, you still cannot call the function from the view--I suspect Angular is treating the return value (the promise itself) as a truthy value, rather than binding to the promise's resolved value. Here's an example of it not working; notice the div is displayed even though the promise is resolving with false.

然而,虽然这消除了摘要周期错误,但您仍然无法从视图中调用该函数 - 我怀疑Angular将返回值(promise本身)视为一个truthy值,而不是绑定到promise的已解析值。这是一个不起作用的例子;请注意,即使promise以false结算,也会显示div。


To use deferred.reject to indicate the user was not authenticated, as in your original service, you'd want to do something more like this in the controller, though I believe that resolveing with false is cleaner:

要使用deferred.reject来指示用户未经过身份验证,就像在原始服务中一样,您希望在控制器中执行更多类似的操作,但我相信使用false进行解析更清晰:

sessionService.isLoggedIn()
  .then(function() {
    $scope.loggedIn = true; // resolved
  }).then(function() {
    $scope.loggedIn = false; // rejected
  });