Angular最佳实践:在工厂或控制器中承诺?

时间:2015-05-05 11:13:10

标签: javascript angularjs promise

我的应用中有一个处理API调用的基本工厂。目前我正在使用表格:

.factory('apiFactory', function($http){

  var url = 'http://192.168.22.8:8001/api/v1/';

  return {
    getReports: function() {
      return $http.get(url+'reports').then(function(result) {
        return result;
      });
    },
    getReport: function(id) {
      return $http.get(url+'report/'+id).then(function(result) {
        return result;
      });
    }
  }
})

在我的控制器中,我正在处理这样的承诺:

.controller('exampleController', function($scope, apiFactory) {

      apiFactory.getReports().then(
        function(answer) {
          if (answer.status==200){
            if (answer.data.status == "error"){
              // DISPLAY ERROR MESSAGE
              console.log(answer.data.msg);
            }
          } else{
            // THROW error
            console.log('error: ', answer);
          }
        },
        function(error){
          console.log('error: ', answer);
        }
      );
    }
  }
})

似乎我可以将承诺处理转移到我的工厂而不是在我的控制器中进行,但我不确定除了较小的控制器之外是否还有其他好处。

有人可以解释有关此模式的最佳做法吗?

3 个答案:

答案 0 :(得分:10)

最终由您决定要向服务的调用者提供多少数据。如果需要,你肯定可以将HTTP响应对象返回给调用者,让他们处理响应(如果承诺得到解决而不是被拒绝,那么,顺便说一句,总是HTTP 2xx)。

但是如果你想将调用者与数据的具体方式隔离开来(可能是缓存,或通过其他机制提供),如果你需要对数据进行后处理,那么建议你处理服务中的回应。

以下是一个例子:

.factory("apiService", function($http, $q){
  var url = 'http://192.168.22.8:8001/api/v1/';

  return {
    getReports: function() {
      return $http.get(url+'reports').then(function(result) {
        var data = result.data;

        if (data === "something I don't accept"){
           return $q.reject("Invalid data");
        }

        var processedData = processData(data);
        return processedData;
      })
      .catch(function(err){
         // for example, "re-throw" to "hide" HTTP specifics
         return $q.reject("Data not available");
      })
    },
    // same idea for getReport
  }
});

然后控制器不需要关心底层机制 - 它得到的只是数据或拒绝。

.controller('exampleController', function($scope, apiService) {
   apiService.getReports()
     .then(function(reports){
        $scope.reports = reports; // actual reports data
     });
})

<强>题外话:

请注意我是如何将服务名称从"apiFactory"更改为"apiService"的。我想指出这一点,以消除可能的误解。无论您使用.factory还是.service还是.value,您获得的注射剂始终是 服务 实例。 .factory只是如何实例化此服务的机制,因此名称"apiFactory"是用词不当。这里唯一的“工厂”是您注册.factory的功能(当然可以是匿名的):

.factory("fooSvc", function fooSvcFactory(){
   return {
      getFoo: function(){...}
   }
})

答案 1 :(得分:2)

最好将所有数据保存在工厂内。这使控制器不受状态影响,并且不再关心工厂的工作方式。如果您更改数据的获取方式(例如,不使用$ http),您的控制器应该不关心,因为它只调用getReport()和

一个很好的解释(参见“解析模型数据,控制器中没有回调arg绑定”): http://toddmotto.com/rethinking-angular-js-controllers/

答案 2 :(得分:2)

简答:处理工厂中的承诺。

<强>为什么吗

如果您在Controller中处理承诺时遇到的问题:

假设您有5个使用相同工厂的控制器。现在让我们假设您希望在未正确解析promise时处理错误。因此,在第一个控制器中,您编写了一个错误回调(或者更准确地说,捕获(异常),因为您正在处理promises),它会显示一条带有错误的警报消息。当promise失败时,此控制器将显示带有错误消息的警报。到现在为止还挺好?对。可是等等!那4个控制器怎么样?你还没有处理它们中的错误。所以现在你最终从第一个控制器&amp;中复制错误处理代码了。将它粘贴在4个控制器的其余部分中。

现在开始有趣了。想象一下,您想要在错误状态下更改逻辑。也许您只想在控制台中记录错误,或者显示烤箱消息。所以你去第一个控制器&amp;更新代码。你认为你完成了吗?没有!!!有4个其他控制器显示警报消息(请记住,您之前粘贴了第一个控制器的代码???)。所以现在,您尝试通过粘贴新的错误消息逻辑来更新其余控制器。想想你需要更新的所有控制器,如果你有更多!累人,不是吗?

为什么要解决工厂中的承诺:

现在让我们说您在服务中编写错误处理逻辑,并且所有控制器都在使用此服务。如果承诺在此服务中失败,您已编写代码以显示警报。如果承诺损坏,所有控制器现在将开始显示此警报。将来,如果您希望更新错误逻辑,只需更新服务中的错误逻辑即可。您的所有控制器都将自动开始使用此更新的逻辑,而您无需付出更多努力。这样,你节省了大量的时间。

所以想一想:

所有控制器中的出厂变化1变化1(可能是1或更多)

我肯定会保证在Factory中进行1次更改,因为这有助于仅使用一次简单的更改重用我的逻辑。我的所有控制器都将开始使用新逻辑。控制器应该是苗条的,精益的和清洁。工厂&amp;服务应该是可重用的。出于这种可重用性的原因,我强烈建议您处理工厂/服务中的承诺