Angular.js передает данные из асинхронной службы в область видимости
У меня есть сервис вроде
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;
});
});
}
}
});
И я хочу сделать из контроллера что-то вроде
function MainCtrl($scope, geolocation) {
geolocation.getCurrentCity(function(city){
$scope.city = city;
});
};
Функция getCurrentPosition отлично работает, и город также определен, но я не знаю, как получить доступ к городу в контроллере.
Что происходит? Когда вызывается getCurrentCity, он вызывает getCurrentPosition для определения gps-коордов. Эти координаты передаются в качестве аргументов для метода onSuccess? Так что это совсем то же самое, что я хочу сделать в методе getCurrentCity, но я не знаю, как это сделать. Асинхронный геокодер извлекает город, я хочу применить новые данные к методу onSuccess.
Любые идеи?
Ответы
Ответ 1
Вы имеете дело с обратными вызовами и асинхронным запросом. Поэтому вы должны использовать службу $q. Просто добавьте его в свою службу с помощью $rootScope и кордовой зависимости.
И добавьте promises к вашей функции, подобной этой
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;
}
И в вашем контроллере сделайте следующее, чтобы выполнить обещание.
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
});
};
Ответ 2
Попробуйте это
function MainCtrl($scope, geolocation) {
$scope.city = null;
geolocation.getCurrentCity(function(city){
$scope.city = city;
if(!$scope.$$phase) {
$scope.$digest($scope);
}
});
};
Иногда наблюдатель не всегда вызывается при инициализации контроллера, заставляя событие дайджеста, если область не находится в фазе, вы можете пойти туда, куда вы хотите пойти, я полагаю.
Дайте мне знать, если я не понимаю ваш вопрос.
О, и я не знаю, правильно ли прочитал код, но кажется, что вы не вызываете обратный вызов onSuccess в своей функции:
замените функцию getCurrentCity следующим образом:
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
Просто вызовите метод onSuccess в службе вместо обработки результата.
getCurrentCity: function (onSuccess, onError) {
this.getCurrentPosition(function (position) {
var geocoder = new google.maps.Geocoder();
geocoder.geocode(options, onSuccess);
});
}
И в вашем контроллере проанализируйте результаты и назначьте город:
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;
});
};
Ответ 4
Исправьте меня, если я ошибаюсь, но представленное решение больше не будет работать полностью, поскольку более новые версии Angular ( > 1.2!?) больше не разворачивают $q promises автоматически.
Таким образом:
$scope.city = geolocation.getCurrentCity();
всегда будет обещанием. Поэтому вам всегда придется использовать:
geolocation.getCurrentCity().then(function(city) {
$scope.city = city;
}