2014-02-23 99 views
12

我的影片用於檢索基於索引(/contacts)個人聯繫(/contacts/:id)的AngularJS服務:增量UI更新

app.service("CollectionService", function($http, $q) { 
    this.fetch = function(collectionURL) { 
     return $http.get(collectionURL).then(function(response) { 
      var urls = response.data; 
      var entities = urls.map(function(url) { 
       return $http.get(url); 
      }); 
      return $q.all(entities); 
     }).then(function(responses) { 
      return responses.map(function(response) { 
       return response.data; 
      }); 
     }); 
    }; 
}); 

// used within the controller: 
CollectionService.fetch("/contacts").then(function(contacts) { 
    $scope.contacts = contacts; 
}); 

結果將顯示在一個簡單的列表(<li ng-repeat="contact in contacts">{{ contact }}</li>)。

但是,由於使用了$q.all,直到收到最後(最慢)響應時才更新該列表。當收到單個聯繫人時,如何從此批量更新切換到增量更新?

回答

5

你可以使用自己的承諾要恢復,然後掛接到通知承諾給您關於整體裝載進度的更新,並仍然使用$q.all來確定何時完成。這基本上是你現在用稍微不同的方式來處理和使用定製的承諾。

小提琴:​​

HTML

<div class="wrapper" ng-app="stackExample"> 
    <div class="loading" ng-show="loading">Loading</div> 
    <div class="contacts" ng-controller="ContactController"> 
     <div class="contact" ng-repeat="contact in contacts"> {{contact.name}} - {{contact.dob}}</div> 
    </div> 
</div> 

控制器

.controller("ContactController", ["$scope", "CollectionService", function (scope, service) { 
    scope.contacts = []; 
    scope.loading = true; 

    service.fetch("/contacts") 
     .then(
    // All complete handler 
    function() { 
     console.log("Loaded all contacts"); 
     scope.loading = false; 
    }, 
    // Error handler 
    function() { 
     scope.error = "Ruh roh"; 
     scope.loading = false; 
    }, 
    // Incremental handler with .notify 
    function (newContacts) { 
     console.log("New contacts found"); 
     scope.contacts = scope.contacts.concat(newContacts); 
    }); 
}]) 

服務

.service("CollectionService", ["$q", "$http", function (q, http) { 

    this.fetch = function (collectionUrl) { 

     var deferred = q.defer(); 

     http.get(collectionUrl) 
     .then(function (response) { 

      // Still map all your responses to an array of requests 
      var allRequests = response.data.map(function (url) { 
       return http.get(url) 
        .then(function (innerResponse) { 
         deferred.notify(innerResponse.data); 
        }); 
      }); 

      // I haven't here, but you could still pass all of your data to resolve(). 
      q.all(allRequests).then(function() { 
       deferred.resolve(); 
      }); 

     }); 

     return deferred.promise; 

    } 

}]); 

你也可以爲你看到網絡連接處理錯誤T和.reject()承諾:

http://docs.angularjs.org/api/ng/service/$q

+1

看起來'notify'正是我正在尋找的東西 - 不確定我以前是如何錯過的。謝謝! – AnC

1

,我想出了一個解決辦法,允許一個onResponse回調被調用爲每個單獨的響應:

var entities = urls.map(function(url) { 
    var request = $http.get(url); 
    if(onResponse) { 
     request.then(function(response) { 
      onResponse(response.data); 
     }); 
    } 
    return response; // this still allows for `$q.all` to handle completion 
}); 

不過,我並不熱衷於API混合回調和承諾 - 讓我保持好奇是否有更優雅的解決方案。

6

您可以將聯繫人列表傳遞到fetch()並讓它彈出列表。

app.service("CollectionService", function($http, $q) { 
    this.fetch = function(collectionURL) { 
     var list = []; 
     $http.get(collectionURL).then(function(response) { 
      var urls = response.data; 
      urls.forEach(function(url) { 
       $http.get(url).then(function(response) { 
        list.push(response.data); 
       }); 
      }); 
     }; 
     return list; 
    }; 
}); 

$scope.contacts = CollectionService.fetch("/contacts"); 
+0

我非常希望避免在服務中的最終數據結構的認識。此外,沒有跡象表明是否所有項目都已被檢索,更不用說錯誤處理了。 – AnC

+0

如果您最終需要通知,則可以始終添加$ q.all()。這也可以讓這個函數的用戶在所有url被檢索後將數據轉換成他們想要的任何數據結構。我也不認爲你的回調解決方案有什麼問題。正如這篇文章(http://solutionoptimist.com/2013/12/27/javascript-promise-chains-2/)文章所述:「Promises允許開發人員輕鬆地將** 1x-only **響應通知附加到任何異步請求/行動。」。所以我不認爲有什麼方法可以在不使用我的解決方案或回調的情況下獲得多個通知。 – rob

0

也許如果你只是返回一個空列表作爲一個結果,並讓服務上的每個succceded要求添加條目它可以工作之前的所有聯繫人

  • 開始顯示的聯繫人被加載
  • 避免回調
  • 知道什麼時候所有聯繫人加載
  • 確保只有服務知道有關數據結構
  • 允許在控制器處理錯誤

app.service("CollectionService", function($http, $q) { 
    this.fetch = function(collectionURL) { 
     return $http.get(collectionURL).then(function(response) { 
      var urls = response.data; 
      var contactPromises = urls.map(function(url) { 
       return $http.get(url).then(function(response) { 
        return $q.when(response.data); 
       }); 
      }); 
      return $q.when(contactPromises); 
     }); 
    }; 
}); 

// used within the controller: 
$scope.contacts = []; 
var addContact = function(contact) { $scope.contacts.push(contact); }; 

CollectionService.fetch("/contacts").then(function(contactPromises) { 
    contactPromises.forEach(function(contactPromise){ 
     contactPromise.then(addContact); 
    }); 
    return $q.all(contactPromise);   
}).then(function(){ 
    alert('All contacts loaded!'); 
}, function(){ 
    alert('Error!!'); 
}); 
+0

我曾考慮過這個問題,但這看起來更像是一種解決方法,而不是我的基於回調的解決方案。 (一方面,沒有任何跡象表明是否所有項目都已被檢索,更不用說錯誤處理了。) – AnC

0

如果我理解正確,你需要一個解決方案,它應該:

app.service("CollectionService", function($http, $q) { 
    this.fetch = function(collectionURL, resultList) { 
     $http.get(collectionURL).then(function(response) { 
      var urls = response.data; 
      urls.forEach(function(url) { 
       $http.get(url).then(function(response) { 
        resultList.push(response.data); 
       }); 
      }); 
     }; 
    }; 
}); 

// used within the controller: 
$scope.contacts = []; 
CollectionService.fetch("/contacts", $scope.contacts);