在控制器内调用$ http promise

时间:2016-07-06 19:56:50

标签: javascript angularjs

提前感谢您的帮助...

我有一个控制器,我用来调用API来发送密码重置链接。我已经将实际的$ http调用抽象为服务以保持控制器很薄。最初我做的是这样的事情:

angular.module('module')
    .service('forgotPasswordService', ['$http', function($http) {
   $http(request).then(function() {
       return {}; //return some object using response 
}]);

我觉得这将是最好的方法,因为它会使控制器保持尽可能薄,并将所有与服务相关的操作分开。我的问题是,从承诺内回来从来没有真正给我任何回报。我必须实际返回$ http服务调用才能获得承诺。我能够完成所有这些工作的唯一方法就是我从控制器内调用.then。

//controller
angular.module('module')
    .controller('forgotPasswordCtrl', ['forgotPasswordService', function(forgotPasswordService) {
forgotPasswordService.forgotPassword(emailAddress).then(function() {
        //do stuff
    }
}]);
//service
angular.module('module')
    .service('forgotPasswordService', ['$http', function($http){
    this.forgotPassword = function(emailAddress) {
        return $http(request);
    };
}]);

这对我来说感觉有点不对,因为控制器现在依赖于从服务接收回复。我可能只是在思考这个问题,但我想提出第二个意见。

这被认为是可接受的/良好做法吗?有没有替代方案可以让我实现我正在寻找的封装?

再次感谢。

3 个答案:

答案 0 :(得分:2)

我以两种略有不同的方式与来自控制器的$http接口。

与您一样,从服务返回$http并与其进行交互感觉不对。

首先,我创建了服务并传递了一个成功方法和一个错误方法(回调)。

// Service
  angular.module('module')
    .service('forgotPasswordService', ['$http', function($http) {

        function sendForgotPasswordEmail(emailAddress, success, error){
            $http.post('/api/v1/resetpassword', {emailAddress:emailAddress})
            .then(success, error);
        }

      return {
        sendForgotPasswordEmail: sendForgotPasswordEmail
      }
}]);

// Controller
angular.module('module')
    .controller('forgotPasswordCtrl', ['forgotPasswordService', function(forgotPasswordService) {

        forgotPasswordService.sendForgotPasswordEmail(emailAddress, 
        function(response){ //success
          // notify user of success
        },
        function(response){ // error
          // notify user of error
        });
}]);

这很有效。我用这种方式创建了一个大型应用程序,但是当我开始第二个大角度项目时,我想知道为什么我隐藏了$http的承诺?

通过传回承诺,我可以使用其他支持promises的库。通过我的第一种方法,我无法利用其他图书馆的承诺支持。

传回$ http承诺

// Service
  angular.module('module')
    .service('forgotPasswordService', ['$http', function($http) {

        function sendForgotPasswordEmail(emailAddress){
          return $http.post('/api/v1/resetpassword', {emailAddress:emailAddress});
        }

      return {
        sendForgotPasswordEmail: sendForgotPasswordEmail
      }
}]);

// Controller
angular.module('module')
    .controller('forgotPasswordCtrl', ['forgotPasswordService', function(forgotPasswordService) {

        forgotPasswordService.sendForgotPasswordEmail(emailAddress)
        .then(
              function(response){ //success
                // notify user of success
              },
              function(response){ // error
                // notify user of error
              });
}]);

答案 1 :(得分:2)

我删除了原来的答案,我觉得自己像是一个傻瓜,说你可以用另一种方式来做。当我第一次开始角度回去检查原始代码时,我发现我在应用程序中调用then()两次 - 一次在我的服务中我返回数据,一次在我的控制器中因为调用{ {1}}返回承诺

事实是,您正在处理异步调用。假设在您的控制器中,您想要这样做:

$http(request).then()

$scope.foo = myService.getFoo(); // No then() 中$ http内的XmlHttpRequest是一个异步调用,意味着它调用它并继续前进。同步选项为deprecated。阻止同步HTTP调用是不良做法,因为它会占用您的用户界面。这意味着您应该在数据准备就绪时使用回调,并且仅为此创建promise API。

如果您绝对不想在控制器中使用getFoo(),我想您可能会将范围绑定参数传递给您的服务,并让您的服务在您的调用中更新它们。我没有尝试过这个,因为它是异步的,我不确定角度是否会知道调用then()所以你可能需要调用digest()如果价值观不会更新。我不喜欢这样,因为我认为范围内值的控制应该由控制器而不是服务处理,但是再次 - 它&# 39;个人喜好。

很抱歉让我误入歧途 - 我遇到了同样的问题,但当我回头看时 - 我看到我使用了一个愚蠢的解决方案。

- 原始答案中的相关陈述 -

请考虑以下事项:

您希望在何处对呼叫进行错误处理,以及谁需要了解它?

您是否需要处理特定控制器中的特定故障,还是可以将它们组合到一个错误处理程序中?对于某些应用程序,我喜欢在特定位置而不是在一般模式对话框中显示错误,但是创建服务来处理所有错误并为用户弹出它们是可以接受的。

您想在哪里处理进度/忙碌指标?

如果您有一个拦截器连接所有http呼叫并广播事件以显示/隐藏繁忙指示器,那么您不必担心在控制器中处理承诺。但是,某些指令将使用promise来显示繁忙指示符,该指示符要求您将其绑定到控制器中的作用域。

对我而言,决定取决于要求和个人选择。

答案 2 :(得分:0)

尝试使用类似的回调:

angular.module('module')
    .service('forgotPasswordService', ['$http', function($http) {
   var recovery = function(request, callback) {
       $http(request).then(function(response) {
           callback(response);
       })
   }
   return { recovery: recovery }
}]);

然后你会这样称呼:

forgotPasswordService.recovery('http://...', function(response){
     console.log(response);
})