【问题标题】:Using factory and http in Angular JS在 Angular JS 中使用工厂和 http
【发布时间】:2014-08-20 21:51:50
【问题描述】:

我刚刚开始使用 angular,发现您可以定义一个工厂以在多个控制器中使用。

我有下面的代码,它在“成功”方法中返回我的结果很好。但不在函数之外。

我感觉它与异步有关,但我不太确定如何继续使用它,如果是这样的话。

任何帮助将不胜感激,因此在此先感谢。

var abyssApp = angular.module('abyssApp', []);

abyssApp.factory('abyssData', function($http) {
    var result,
        products = $http.get('http://abyss.local/products.php');

    products.success(function(data) {
        result = angular.fromJson(data);
        console.log('success: ', result);  // object with data in
    });

    console.log(result);  // undefined
});

abyssApp.controller('ItemListCtrl', function($scope, abyssData) {
    $scope.items = abyssData;
    console.log('abyssData: ', abyssData);  // undefined
});

【问题讨论】:

    标签: javascript angularjs http factory


    【解决方案1】:

    这里有一些修改。请注意,我是在没有真正检查的情况下这样做的,所以打字和错误是完全可能的,但它的要点应该是有意义的。

    var abyssApp = angular.module('abyssApp', []);
    
    abyssApp.service('abyssData', function($http) {
        this.products = $http.get('http://abyss.local/products.php');
    });
    
    abyssApp.controller('ItemListCtrl', function($scope, abyssData) {
        abyssData.products.then(function(data) {
            console.log(data);
            $scope.items = data;
        }).catch(function(err){console.log(err);});
    });
    

    我把它变成了一个服务而不是一个工厂,因为你真的只需要一个服务(单件)。如果您要调用不同的 URL,并且想要执行诸如 product = new abyssData('potatoes') 之类的操作,则工厂将是合适的。

    一般来说,$http 返回一个promise 对象,它本身并不是你需要的数据,而是解析你需要的数据。这种承诺模式在 Angular 中重复了很多次(并且在其他 javascript 场所越来越流行,因为它很棒(IMO))。这意味着,在 promise 对象上,您调用 .then() 方法并传入一个函数,以便在 promise 解决时调用。 $http 使用 XHR 的结果解析。其他承诺会与其他事情一起解决。

    在 promise .then 堆栈的末尾,最好调用 .catch() ,因为在 promise 中抛出的异常通常会进入 bitbucket,除非您明确查找它们。这是 Promise 工作方式的普遍缺陷,意味着您必须小心例外情况。对于较旧的 JS 引擎,您需要将其更改为 .then()'catch',因为 catch 在某些地方是一个有趣的词。但我有 90% 的把握,任何阻塞 .catch 的 JS 环境无论如何都不会运行 Angular。

    【讨论】:

    • 感谢您的解释!我只需要在您的示例中将return this.products 添加到服务中。
    【解决方案2】:

    在“ItemListCtrl”中,您应该注入从“abyssData”工厂创建的对象。然后,它应该返回 $http 承诺并直接在控制器中,在成功回调中,您应该将返回的结果分配给 $scope.items。这只是一种示例方法,关于如何在 Angular 中解决此类问题有很多方法和模式。

    console.log(result);  // undefined
    

    记录“未定义”,因为当您调用此行时,还没有响应。试着弄清楚什么是 promise 以及 AJAX 调用的工作原理,尤其是应该定义什么“成功”和“错误”。

    【讨论】:

      【解决方案3】:

      大概三件事:

      1) 显式声明依赖:

      abyssApp.controller('ItemListCtrl', function($scope, abyssData) {
      

      abyssApp.controller('ItemListCtrl', ['$scope', 'abyssData', function($scope, abyssData) {
      

      2) 要使用你想要的值,工厂必须返回那个值,所以最后你应该返回“结果”变量。 尝试更改 xhr 请求中的常用变量名称“result”以防止覆盖。

      3) 为防止在返回前访问数据,尽量给出方法而不是值:

      abyssApp.factory('abyssData', function($http) {
          return {
              get: function(successCallback){ //your callback function
                  $http.get('http://abyss.local/products.php')
                      .success(function(){
                          if(successCallback){
                              successCallback.apply(arguments);
                          }
                      });
              }
          }
      }
      

      要使用改变这个:

      $scope.items = abyssData;
      

      为此

      abyssData.get(function(data){
          $scope.items = data;
      });
      

      【讨论】:

        【解决方案4】:

        您需要从工厂公开一些数据或方法,以便控制器可以访问它。目前工厂没有暴露任何东西。

        根据您的用例,我建议如下:

        var abyssApp = angular.module('abyssApp', []);
        
        abyssApp.factory('abyssData', function($http) {
            function abyssData() {
                var self = this;
                self.products = null;
        
                self.getProducts = function(){
                    // return products if they are set already (cache)
                    if (self.products) { return self.products; }
                    $http.get('http://abyss.local/products.php').success(function(data){
                        self.products = data;
                        return self.products;
                    });
                };
            }
            return new abyssData();
        });
        
        abyssApp.controller('ItemListCtrl', function($scope, abyssData) {
            $scope.items = abyssData.getProducts();
        });
        

        但是,这两种方法似乎都不需要工厂来封装这样一个简单的查询?

        【讨论】:

          【解决方案5】:

          反之亦然。首先,工厂应该返回一个值。该值是一个单例。这意味着工厂执行后,不会多次执行自身。您可以做的是返回一个对您的结果有引用的对象。

          abyssApp.factory('abyssData', function($http) {
              var ret = {},
                  products = $http.get('http://abyss.local/products.php')
          
              products.success(function(data) {
                  ret.result = angular.fromJson(data);
                  console.log('success: ', result);  // object with data in
              });
          
              console.log(result);  // undefined
          
              return ret;
          });
          
          abyssApp.controller('ItemListCtrl', function($scope, abyssData) {
              $scope.items = abyssData.result;
              console.log('abyssData: ', abyssData);  // undefined
          });
          

          但是如果这个解决方案闻起来不太好。除非您的对象列表永远不会更新。返回可以在需要时查询的资源会更有意义。不确定控制器何时加载,结果是否已经加载。这意味着访问 abyssData.result 可能会先返回 undefined,然后再返回其他内容。

          但老实说,你要做的应该是这样:

          https://docs.angularjs.org/api/ngResource/service/$resource

          你可以有这样的工厂:

          abyssApp.factory('AbyssData', ['$resource', function($resource) {
              return $resource('http://abyss.local/products.php')
          }])
          
          abyssApp.factory('abyssData', ['AbyssData', function(AbyssData) {
             return AbyssData.query();
          }])
          

          看看这个:https://docs.angularjs.org/api/ngResource/service/$resource

          【讨论】:

          • 感谢您的解释。从中,我了解了工厂的工作原理,我应该在工厂中返回 $http.get,然后在控制器中处理它的方法(成功、错误等)。
          • 这是你可以做的,但如果你想再次重用工厂,返回 get 请求可能没有用。我不确定成功承诺是否会多次执行。
          • 你应该做的是使用ngResource
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-04-29
          • 1970-01-01
          • 2017-05-05
          • 2014-07-09
          • 1970-01-01
          • 2017-07-06
          相关资源
          最近更新 更多