在AngularJS 1.4中返回另一个后,对$ resource进行多次调用

时间:2015-06-07 09:27:45

标签: angularjs angularjs-resource angular-promise

我正在尝试找到一种最佳实践,以便在返回第一个函数时多次调用$ resource。

考虑以下架构:我们有一个作者数据库,它有书籍,有多个标签(sf,惊悚片,恐怖片......)。

angular.module('myApp',[ngResource])
    .controller('myCtrl',['$scope', function($scope){
        $scope.results = [];

        apiService.Authors.getAll().$promise                                         //return array of authors
            .then(function(authors){
                $scope.results.push(authors);
                angular.forEach(authors,function(akey,author){
                    apiService.BooksBy.getAll({authorID:author.id}).$promise         //return array of books
                        .then(function(books){
                            $scope.results[akey].books = [];
                            $scope.results[akey].books.push(books);
                            angular.forEach(books,function(bkey,book){
                                apiService.Tags.getAll({bookID:book.id}).$promise    //return array of tags
                                    .then(function(tags){
                                        $scope.results[akey].book[bkey].tags = [];
                                        $scope.results[akey].book[bkey].tags.push(tags);
                                    });
                            });
                        });
                });
            });
    }]);

这是进行多个嵌套调用的唯一方法吗?

我正在考虑更多扁平并且更具可读性的东西,比如带有链式承诺的解决方法。

任何想法都会受到欢迎,因为我无法找到更好的方法。

问候!

2 个答案:

答案 0 :(得分:0)

您可以使用$ q拨打多个电话。它是调用多个调用的最佳方式,如果所有调用都是return promises,则结束调用

如果您根据先前结果的呼叫呼叫多个呼叫,那么这是执行此操作的最佳方式。

static void Main(string[] args)
    {
        int max, i, count;
        i = 0; // this is used to keep trck of how many primes have been added to the array
        count = 0; // this is used to test each number
        Console.WriteLine("This will work out the first x prime numbers with x being the number of prime numbers you want");
        Console.WriteLine("Enter the number of prime numbers you want.");
        max = Convert.ToInt32(Console.ReadLine());

        int[] primes = new int[max];

        while (i <= max)
        {
         while (count <= 9999)
         {
             if (count % 2 == 0 || count % 3 == 0 || count % 5 == 0 || count % 7 == 0 ) // tests if count number is a prime 
             {
                 if (count == 2 || count == 3 ||count == 5 ||count == 7 ) // ensures 2,3,5,7 are added to primes if neccesarry
                 {

                     primes[count] = count; //add to array
                     i++; // increments the count on the number of prime numbers
                 }
                 count ++; // increments the count 
                 break;
             }
             else
             {
                 primes[count] = count;
                 i++;
                 count ++;
             }
         }
        }

        Console.WriteLine("The first {0} prime numbers are ... ", max);
        foreach(var item in primes)
        {
            Console.Write(item.ToString() + ", ");
        }
    }

答案 1 :(得分:0)

这是一个可能的解决方案:如果你使用$q.all,但是对于你推送到数组的每个承诺,你添加一个'sub' - .then,你可以包装每个人的结果api调用并将其与您需要由下一个.then - 处理程序使用的密钥(或其他任何内容)一起传递。

我试图通过一些更改,非常密切地反映原始代码示例:

  • 我创建了一个小型的mockApiService,但为了简单起见,我捏造了几个调用
  • 我已经用命名函数替换了内联.then - 处理程序,以更清楚地证明我们已经将链条弄平了
  • 我正在使用angular.extend更新范围上的json结构,该结构显示在<pre>标记内。您的原始代码示例正在使用数组执行某些操作,但我不确定它是否会按预期工作 - 我使用angular.extend意味着接近我认为您的目的。
  • 在我的applyBooksGetTags函数中,我们尝试同时传递akeybkey以及结果。但是,akey实际上来自之前的结果 - 以result.akey访问。这存在于我们用于编译promise数组的angular.forEach循环之外的范围内 - 当forEach函数执行时,result.akey将被外部{{1}更改循环。出于这个原因,我在一个自动执行的函数中封装了对forEach的调用 - 一个闭包,以确保在每个事情发生时保持for的值。
  • 原始样本中的result.akey次调用可能已经交换了(值,键)参数的顺序 - 因此已在下面的代码段中进行了交换
  • 有很多angular.forEach个评论...这主要是因为代码片段'整理'功能使代码有点难以理解;遗憾!

} //end blah blah
(function() {
  "use strict";

  angular.module('myApp', [])
    .controller('myCtrl', ['$scope', '$q', 'apiService', MyCtrl])
    .service('apiService', ['$timeout', mockApiService]);

  function MyCtrl($scope, $q, apiService) {
      $scope.results = {};

      apiService.Authors.getAll().$promise
        .then(applyAuthorsGetBooks)
        .then(applyBooksGetTags)
        .then(applyTags);

      function applyAuthorsGetBooks(authors) {
          var promises = [];
          angular.extend($scope.results, authors);

          angular.forEach(authors, function(author, akey) {
            promises.push(apiService.BooksBy.getAll({
                authorID: author.id
              }).$promise
              .then(function(books) {
                return $q.when({ // < MAGIC HERE: return the books, but also the aKey
                  books: books,
                  akey: akey
                }); //end return
              }) //end then
            ); //end push
          }); //end foreach

          return $q.all(promises);
        } // end authorsGetBooks

      function applyBooksGetTags(results) {
          var promises = [];

          for (var i = 0; i < results.length; i++) {
            var result = results[i];
            $scope.results[result.akey].books = angular.extend({}, result.books);

            (function(akey) { //anon func to wrap the current value of akey in a closure, so when inner loop accesses it hasn't been changed by outer loop
              angular.forEach(result.books, function(book, bkey) {
                promises.push(apiService.Tags.getAll({
                    bookID: book.id
                  }).$promise
                  .then(function(tags) {

                    return $q.when({ // < MAGIC HERE, again: return the tags, but also the bkey and akey
                      tags: tags,
                      akey: akey,
                      bkey: bkey
                    }); //end return
                  }) //end then
                ); //end push

              }); //end foreach
            })(result.akey) //pass our current akey value to our anon func

          } //end for
          return $q.all(promises);
        } //end booksGetTags

      function applyTags(results) {
        for (var i = 0; i < results.length; i++) {
          var result = results[i];
          $scope.results[result.akey].books[result.bkey].tags = angular.extend({}, result.tags);
        } //end for
      }

    } //end MyCtrl

  function mockApiService($timeout) {
      function _simulateResource(data) {
          return {
            $promise: $timeout(function timeoutHandler() {
                return data;
              }, 1000) //end $timeout
          }; //end return
        } //end _simulateResource()

      return {
        Authors: {
          getAll: function authorsGetAll() {
              return _simulateResource({
                Author1: {
                  id: 'Author 1'
                },
                Author2: {
                  id: 'Author 2'
                }
              }); //end return
            } //end getAll
        }, //end Authors
        BooksBy: {
          getAll: function booksByGetAll(params) {
              var authorId = params.authorID;
              return _simulateResource({
                Book1: {
                  id: 'Book 1 by ' + authorId
                },
                Book2: {
                  id: 'Book 2 by ' + authorId
                }
              }); //end return
            } //end getAll
        }, //end BooksBy
        Tags: {
          getAll: function tagsGetAll(params) {
              var bookId = params.bookID;
              return _simulateResource({
                Rom: {
                  id: 'Rom'
                },
                Zom: {
                  id: 'Zom'
                },
                Com: {
                  id: 'Com'
                }
              }); //end return
            } //end getAll
        } //end Tags
      }; //end return
    } // end MockService API
})();