javascript Angular - 绑定到返回承诺的函数

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/16366516/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-27 04:20:34  来源:igfitidea点击:

Angular - Bind to function that returns a promise

javascriptangularjs

提问by Scott

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

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

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?

我究竟做错了什么?

采纳答案by Michelle Tilley

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

您正在解决您的承诺,但没有价值 - 因此$scope解决时的承诺价值是undefined,这是假的,因此您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 JSFiddledemonstrating resolving the deferred with a truthy value and binding to the $scope.

这是一个 JSFiddle,演示了使用真实值解决延迟并绑定到$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 differentpromise each digest cycle (which is why you were getting the '10 digest cycles' error). You could, however, ensure that extra calls to sessionService.isLoggedInreturns the samepromise instead of creating a new one, since you can call thenon a promise multiple times (and in fact this is one of the benefits of promises):

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

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.authenticatedboolean, 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 divis displayed even though the promise is resolving with false.

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



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

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

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