2014-10-07 64 views
1

這是我工作的代碼。

appControllers.controller('MyaSellerOrderCtrl', ['$scope', '$rootScope', 'Order', '$http', 
     function($scope, $rootScope, Order, $http) { 
      $scope.results = []; 
      $scope.getData = function() { 
        $http.get('api/orders/business/?user_id=' + $rootScope.user.user_id).success(function(data){ 
         for (var i = 0; i < data.length; i++) { 
          $http.get('api/orders/seller/?business_id=' + data[i].business_id).success(function(data1){ 
           // console.log(data1);   
           $scope.results[i] = data1; 
          }); 
         } 
         console.log($scope.results); 
        }); 
      }; 
      $scope.getData(); 
     }]); 

問題是,$ scope.results是空的,而功能正常工作。有人說這是由於$ http的異步性質。你能修改代碼來使用promise來避免錯誤嗎?

現在我更新的代碼所示

appControllers.controller('MyaSellerOrderCtrl', ['$scope', '$rootScope', '$http','$q', 
    function($scope, $rootScope, $http, $q) { 

     $scope.results = []; 

     function _getOrdersById(id) { 
      return $http.get('api/orders/business/?user_id=' + id); 
     } 

     function _parseOrders(orders) { 
      var _promises = []; 

      orders.forEach(function (order, index) { 
       var _promise = $http.get('api/orders/seller/?business_id=' + order.business_id).then(function (response) { 
        $scope.results[index] = response; 
       }); 

       _promises.push(_promise); 
      }); 

      return $q.all(_promises); 
     } 

     $scope.getData = function() { 
      _getOrdersById($rootScope.user.user_id) 
       .then(_parseOrders) 
       .then(function() { 
        console.log($scope.results); 
       }, function (error) { 
        console.error(error); 
       }); 
     }; 


     $scope.getData(); 

    } 
]); 

,但它仍然顯示錯誤

console output

159線點到線

orders.forEach(function(order,index) { 
+0

$ http已經是一個承諾。 – 2014-10-07 16:36:57

回答

1

在您的使用案例中,console.log($scope.results)來得太早。

如果你做了「正確的方式」或這個「不要在家裏做,因爲它是愚蠢的」方式,你會得到結果。

錯誤辦法做到這一點,只是在這裏告訴你:

var MyCtrl = app.controller(function($scope, $timeout, $http, $rootScope) { 
     $scope.results = []; 
     $scope.getData = function() { 
       $http.get('api/orders/business/?user_id=' + $rootScope.user.user_id).success(function(data){ 
        for (var i = 0; i < data.length; i++) { 
         $http.get('api/orders/seller/?business_id=' + data[i].business_id).success(function(data1){ 
          // console.log(data1);   
          $scope.results[i] = data1; 
         }); 
        } 

        // THIS is the part I'm changing: 
        $timeout(function() { 

         console.log($scope.results); 
        }, 10000); 
       }); 
     }; 
     $scope.getData(); 
    }); 

只是假設在這裏,但我認爲這應該工作。爲什麼?因爲我們在10秒鐘後記錄結果,所有單個訂單都被提取。

您的版本立即在$http個別訂單的任何請求已完成之前稱爲console.log()

稍微好一點的方法是按其他人的建議做$q.all()。而更好的辦法是將這一切都轉化爲服務。

稍好的方式做你的事:

// Controller. Notice how simplified it seems now, comparing to the original 
// version. We're just passing results to and from the $scope, which is what 
// controllers should do. 

var MyController = app.controller(function($scope, OrderService, $rootScope) { 

    OrderService.getOrders($rootScope.user.user_id) 
    .then(function(results) { 

     // only putting $scope.results live here, you can have 
     // an ng-show="$scope.results.length" or something to show a spinner 
     // or similar while loading. 
     $scope.results = results; 
    }); 
}); 

// Then, your service would do all the work 

var OrderService = app.service(function($http) { 

    // we're going to play it with a service, just copy/pasting Lowe's answer here 
    // and modifying a few bits to take out controller part 

    var _results = []; 

    // This returns a promise. Angular can accept a promise and will wait until resolved. 
    function _getOrdersById(id) { 
     return $http.get('api/orders/business/?user_id=' + id); 
    } 

    // This also returns a promise. 
    function _parseOrders(orders) { 
     var _promises = []; 

     orders.forEach(function (order, index) { 
      var _promise = $http.get('api/orders/seller/?business_id=' + order.business_id).then(function (response) { 
       _results[index] = response; 
      }); 

      _promises.push(_promise); 
     }); 

     return $q.all(_promises); 
    } 

    // Anything attached to _this_ is "public" for a service 
    this.getOrders = function(userId) { 

     // Create a defered object to return to callers 
     var d = $q.defer(); 

     // Get that first promise 
     _getOrdersById(userId) 

     // the next promise in order (_parseOrders), will receive whatever 
     // _getOrdersById return 
     .then(_parseOrders) 

     // The next promise won't be called until $q.all of _parseOrders is called. 
     .then(function() { 

      // finally resolve our original promise. This returns to 
      // the caller of the service 
      d.resolve(_results); 

     }, function (error) { 

      console.error(error); 
      d.reject(error); 
     }); 

     // this return happens almost before any of the $http calls above. 
     // But since you're returning a promise, angular knows this it have to wait 
     // until that d.resolve() or d.reject() somewhere in the async response handlers. 
     return d.promise; 

    }); 

可能有更好的方式來打破這種下來,但你要問更具體一點的問題。

3

與您的代碼的問題是你立即試圖獲取數據的事實聲明console.log($scope.results);。我建議你閱讀異步編程的真實內容,因爲它可以幫助你更好地理解爲什麼會發生這種情況。 $http根據AngularJS文檔(這只是一個修改的承諾)已經返回HttpPromise,所以沒有意義改變它。

問題是,使$http調用需要時間,並且您正在用for循環遍歷它(這是不好的做法,您應該考慮$q.all())。如果你只有同步編程經驗,你可能會認爲一旦for循環完成就會發生console.log($scope.results);,但事實並非如此。 for循環中的調用將會異步,並且在您回答問題之前您將進行日誌記錄。

TL; DR:你真的需要研究javascript和你甚至可以從服務器:)

appControllers.controller('MyaSellerOrderCtrl', ['$scope', '$rootScope', 'Order', '$http', '$q', 
function($scope, $rootScope, Order, $http, $q) { 
    $scope.results = []; 
    $scope.getData = function() { 
     $http.get('api/orders/business/?user_id=' + $rootScope.user.user_id) 
      .success(function(data){ 

      var promises = data.map(function (item) { 
       return $http.get('api/orders/seller/?business_id=' + item.business_id); 
      }); 

      $q.all(promises).then(function(data) { 

       //Use this to see what the data is 
       console.log(data); 
       //Add some logic here if data isn't exactly the array you want 
       $scope.results = data; 

      }, function (err) { 
       //Do some error handling 
      }); 

     }); 
    }; 
    $scope.getData(); 
}]); 
+0

我明白成功是異步的。但我需要成功功能與每次迭代同步工作。我會怎麼做,這樣我就可以得到每個迭代的控制檯輸出 – 2014-10-07 16:57:41

+0

我肯定會建議按照我所說的去查看'$ q.all'。如果你正確地實現了它,你將能夠在每次迭代中做一些邏輯,並且得到最終的總結果(這取決於你的需要,儘管如此很難給出一個通用的例子)。 – 2014-10-07 17:32:02

+0

更新了帖子。請檢查它 – 2014-10-08 08:38:21

1

可以通過嵌套承諾讓事情得到一個響應返回之前記錄的變量異步的概念有點結構。既然你想做第一個API調用,並且一次在結果中每行完成一次,你還需要使用$ q服務,就像克里斯托弗提到的一樣。

我還沒有測試過這段代碼,但它希望能給你一些關於我在說什麼的想法。

$scope.results = []; 

function _getOrdersById(id) { 
    return $http.get('api/orders/business/?user_id=' + id); 
} 

function _parseOrders(orders) { 
    var _promises = []; 

    // Stop parsing if orders isn't an array 
    if (!angular.isArray(orders)) { 
     return; 
    } 

    orders.forEach(function (order, index) { 
     var _promise = $http.get('api/orders/seller/?business_id=' + order.business_id).then(function (response) { 
      $scope.results[index] = response; 
     }); 

     _promises.push(_promise); 
    }); 

    return $q.all(_promises); 
} 

$scope.getData = function() { 
    _getOrdersById($rootScope.user.user_id) 
     .then(_parseOrders) 
     .then(function() { 
      console.log($scope.results); 
     }, function (error) { 
      console.error(error); 
     }); 
}; 
+0

謝謝。但是這也顯示了錯誤。 – 2014-10-08 08:34:04

+0

我已通過修改和錯誤更新了帖子。 – 2014-10-08 08:37:52

+0

checkoutController.js在線159上的內容是什麼?你正在使用什麼版本的AngularJs? – 2014-10-08 09:06:52