2013-07-16 9 views
11

Ho un servizio comeAngular.js passare i dati da un servizio asincrono a portata

app.factory('geolocation', function ($rootScope, cordovaReady) { 
    return { 
     getCurrentPosition: cordovaReady(function (onSuccess, onError, options) { 

      navigator.geolocation.getCurrentPosition(function() { 
       var that = this, 
        args = arguments; 

       if (onSuccess) { 
        $rootScope.$apply(function() { 
         onSuccess.apply(that, args); 
        }); 
       } 
      }, function() { 
       var that = this, 
        args = arguments; 
       if (onError) { 
        $rootScope.$apply(function() { 
         onError.apply(that, args); 
        }); 
       } 
      }, options); 
     }), 
     getCurrentCity: function (onSuccess, onError) { 
      this.getCurrentPosition(function (position) { 
       var geocoder = new google.maps.Geocoder(); 
       geocoder.geocode(options,function (results, status) { 
        var city = address_component.long_name; 
       }); 
      }); 
     } 
    } 
}); 

E voglio fare da un controller di qualcosa come

function MainCtrl($scope, geolocation) { 
    geolocation.getCurrentCity(function(city){ 
     $scope.city = city; 
    }); 
}; 

Il getCurrentPosition funziona benissimo e la città è determinato anche, ma non so come accedere alla città nel controller.

Cosa succede? Quando viene chiamato getCurrentCity, richiama getCurrentPosition per determinare i gps coords. Queste coordinate sono passate come argomenti al metodo onSuccess giusto? Quindi questo è esattamente lo stesso che voglio fare con il metodo getCurrentCity, ma non so come. Un geocoder asincrono ha recuperato la città, voglio applicare i nuovi dati al metodo onSuccess.

Qualche idea?

risposta

29

Avete a che fare con i callback e richiesta asincrona. Quindi dovresti usare il servizio $q. Inseriscilo nel tuo servizio con la dipendenza $ rootScope e cordovaReady. E aggiungere le promesse per la funzione come questo

getCurrentCity: function() { 
    var deferred = $q.defer(); 
    this.getCurrentPosition(function (position) { 
     var geocoder = new google.maps.Geocoder(); 
     geocoder.geocode(options,function (results, status) { 
     var city = address_component.long_name; 
     $rootScope.$apply(function(){ 
      deferred.resolve(city); 
     }); 
     }); 
    }); 
    return deferred.promise; 
} 

E nel controller, effettuare le seguenti operazioni per gestire la promessa.

function MainCtrl($scope, geolocation) { 
    geolocation.getCurrentCity().then(function(result) { //result === city 
    $scope.city = result; 
    //do whatever you want. This will be executed once city value is available 
    });  
}; 
+0

Risolto il problema, avevo un filtro che causava il loop. –

+0

Il primo approccio nell'implementazione del controller precedente di impostare una proprietà $ scope su una promessa non funziona più in Angular 1.3.Le promesse non vengono più scartate, quindi è necessario utilizzare il secondo approccio (impostando la proprietà $ scope nel gestore allora) – jbandi

+0

@jbandi: sembra suggerire che qualcosa è cambiato. Non ho ottenuto qual è il cambiamento esatto. Ti dispiace chiarirlo per me? Aggiornerò la risposta di conseguenza. –

4

Basta chiamare il metodo onSuccess nel servizio invece di gestirne il risultato.

getCurrentCity: function (onSuccess, onError) { 
    this.getCurrentPosition(function (position) { 
     var geocoder = new google.maps.Geocoder(); 
     geocoder.geocode(options, onSuccess); 
    }); 
} 

E nel controller, analizzare i risultati e assegnare la città:

function MainCtrl($scope, geolocation) { 
    geolocation.getCurrentCity(function(results, status){ 
     // Parse results for the city - not sure what that object looks like 
     $scope.city = results.city; 
    }); 
}; 
5

Prova questa

function MainCtrl($scope, geolocation) { 
    $scope.city = null; 
    geolocation.getCurrentCity(function(city){ 
     $scope.city = city; 
     if(!$scope.$$phase) { 
      $scope.$digest($scope); 
     } 
    }); 
}; 

A volte, l'osservatore non è sempre chiamato quando instanciating il controller, forzando un evento digest, se il campo di applicazione non è in fase, si può andare a dove si vuole per andare, credo. Fammi sapere se non ho capito la tua domanda.

Oh, e non so se ho letto il codice properrly ma sembra che non si sta chiamando la callback onSuccess nella funzione: sostituire la funzione getCurrentCity da questo:

getCurrentCity: function (onSuccess, onError) { 
     this.getCurrentPosition(function (position) { 
      var geocoder = new google.maps.Geocoder(); 
      geocoder.geocode(options,function (results, status) { 
       var city = address_component.long_name; 
       if(typeof onSuccess === 'function') { 
        onSuccess(city); 
       } 
      }); 
     }); 
    } 
3

Correggetemi se ho torto, ma la soluzione presentata non funzionerà più completamente, poiché le versioni Angular più recenti (> 1.2 !?) non scartano più automaticamente $ q promette automaticamente.

Così:

$scope.city = geolocation.getCurrentCity(); 

sarà sempre una promessa. Quindi dovrai sempre usare:

geolocation.getCurrentCity().then(function(city) { 
    $scope.city = city; 
} 
Problemi correlati