2013-08-09 11 views
7

L'impostazione: Desidero disporre di un servizio che più controllori possano eseguire query per i dati estratti utilizzando $ http. La soluzione iniziale era usare le promesse come suggerito here.

Il problema: Ogni volta che un controller interroga il servizio, il servizio restituisce una promessa $ http, generando più query che estraggono gli stessi dati da un server remoto, più e più volte.

A Soluzione: la funzione di servizio restituisce dati o una promessa come di seguito. E spetta al controller controllare e agire di conseguenza.

app.factory('myService', function($http) { 
    var items = []; 
    var myService = { 
     getItems: function() { 
      // if items has content, return items; otherwise, return promise. 
      if (items.length > 0) { 
       return items; 
      } else {  
       var promise = $http.get('test.json').then(function (response) { 
        // fill up items with result, so next query just returns items. 
        for(var i=0;i<response.data.length;i++){ 
         items.push(response.data[i]); 
        } 
        return items; 
       }); 
       // Return the promise to the controller 
       return promise; 
      } 
    }; 
    return myService; 
}); 

Così, quando un controllore ha bisogno di tali dati, il controllore fa proprio qualcosa di simile:

app.controller('MainCtrl', function(myService,$scope) { 
    var promiseOrData = myService.async(); 
    // Check whether result is a promise or data. 
    if (typeof promiseOrData.then === 'function'){ 
     // It's a promise. Use then(). 
     promiseOrData.then(function(data){ 
      $scope.data = data; 
     }); 
    } else { 
     // It's data. 
     $scope.data = data; 
    } 
}); 

Quindi la domanda è: Esiste un modo migliore di fare questo? Con molti controller, questo metodo avrebbe un sacco di codice duplicato. Idealmente, i controllori interrogheranno direttamente il servizio per i dati direttamente.

Grazie!

risposta

12

$ http restituisce una promessa, possiamo usarlo invece di crearne uno nuovo con $ q. Una volta risolta la promessa, possiamo continuare a restituirla.

.factory('myService', ['$http','$q', function($http, $q) { 
    var items = []; 
    var last_request_failed = true; 
    var promise = undefined; 
    return { 
     getItems: function() { 
      if(!promise || last_request_failed) { 
       promise = $http.get('test.json').then(
       function(response) { 
        last_request_failed = false; 
        items = response.data; 
        return items; 
       },function(response) { // error 
        last_request_failed = true; 
        return $q.reject(response); 
       }); 
      } 
      return promise; 
     }, 
    }; 
}]) 

nel controller:

myService.getItems().then( 
    function(data) { $scope.data = data; } 
); 
+0

Bel lavoro. Ancora meglio riduce la quantità di codice necessaria nel controller. –

+0

Un modo per rendere questo più ASCIUTTO dal lato dei servizi? Ho diversi endpoint http che a voglio fare questo e ripetendo questo codice per ogni servizio/http endpoint sarebbe fuori mano –

+0

@KyleKochis, l'unica cosa che sono stato in grado di fare è creare un singolo [intercettore HTTP] (http://stackoverflow.com/a/11957760/215945) per rilevare gli errori HTTP per tutti i miei servizi, quindi il codice precedente non ha più bisogno di un gestore degli errori. –

1

Crea la tua promessa che risolve i dati memorizzati nella cache o i dati recuperati.

app.factory('myService', function($http, $q) { 
    var items = []; 
    var myService = { 
     getItems: function() { 
      var deferred = $q.defer(); 

      if (items.length > 0) { 
       //resolve the promise with the cached items 
       deferred.resolve(items); 
      } else {  
       $http.get('test.json').then(function (response) { 
        // fill up items with result, so next query just returns items. 
        for(var i=0;i<response.data.length;i++){ 
         items.push(response.data[i]); 
        } 
        //resolve the promise with the items retrieved 
        deferred.resolve(items); 
       },function(response){ 
        //something went wrong reject the promise with a message 
        deferred.reject("Could not retrieve data!"); 
       }); 


      } 
     // Return the promise to the controller 
     return deferred.promise; 
    }; 
    return myService; 
}); 

Quindi consumare la promessa nel controller.

app.controller('MainCtrl', function(myService,$scope) { 
    var promiseOrData = myService.getItems(); 

     promiseOrData.then(function(data){ 
      $scope.data = data; 
     }, 
     function(data){ 
      // should log "Could not retrieve data!" 
      console.log(data) 
     }); 

}); 
Problemi correlati