В моем приложении есть концепция Event, которая имеет атрибуты широты и долготы. На стороне клиента я хотел бы использовать navigator.geolocation для вычисления текущего расстояния до каждого события и отображения его в представлении.
Ниже я до сих пор. Он работает, но он застревает в бесконечном цикле (ошибка ниже).
factory('ys$currentPosition', ['$q', '$window', function($q, $window){
'use strict';
return {
position: function(){
var deferred = $q.defer();
if ($window.navigator.geolocation) {
$window.navigator.geolocation.getCurrentPosition(
function (position) {
deferred.resolve(position);
},
function (err) {
deferred.reject(err);
}
);
} else {
deferred.reject('Geolocation not supported.');
}
return deferred.promise;
}
};
}]).
factory('Event', ['$resource', 'ys$currentPosition', function($resource, ys$currentPosition){
'use strict';
var Event = $resource($server_hostname + '/api/v4/organizations/' + $org_id + '/events/:id', {}, {});
Event.prototype.distance = function(){
var this_event = this;
return ys$currentPosition.position().then(function(position){
return getDistanceFromLatLonInMi(
position.coords.latitude,
position.coords.longitude,
this_event.latitude,
this_event.longitude
)
});
}
return Event;
}]).
View (in HAML):
%ys-index-row-right{"ng-if" => "item.distance()"}
%i.fa.fa-map-marker
%span
{{ item.distance() }} mi
Вот эта ошибка:
angular-0195028….js?body=1:69 Uncaught Error: [$rootScope:infdig] 10 $digest() iterations reached. Aborting!
Watchers fired in the last 5 iterations: [[{"msg":"item.distance()","newVal":{},"oldVal":{}}],[{"msg":"item.distance()","newVal":{},"oldVal":"..."}],[{"msg":"item.distance()","newVal":{},"oldVal":"..."}],[{"msg":"item.distance()","newVal":{},"oldVal":"..."}],[{"msg":"item.distance()","newVal":{},"oldVal":"..."}]]
Plunker:
Я "исправил" это, признав, что вы не можете ожидать, что представление развяжет обещание. Таким образом, внутри контроллера просто повторяется над $ scope.events и называется методом, который 1) развязал обещание. 2) прямое значение расстояния на объекте.
Поэтому я увидел бесконечную ошибку цикла, если я выбрал "block", когда браузер попросил геолокацию. Если бы я сказал "Разрешить", это сработало. Сказав, что я думаю, что код можно было бы убрать и разбивать на отдельные единицы операции, чтобы упростить отслеживание ошибки и сделать приложение более читаемым. Прикрепленный подход, который я, возможно, принял: Plunker Here
просто код script.js здесь
var app = angular.module('app', ['ngMockE2E', 'ngResource', 'controllers']);
app.run(function($httpBackend) {
events = [{title: 'hello world', latitude: '38', longitude: '-77'}];
$httpBackend.whenGET('/foos').respond(events);
});
app.factory('ys$currentPosition', ['$q', '$window', function($q, $window){
'use strict';
return {
position: function(){
var deferred = $q.defer();
if ($window.navigator.geolocation) {
$window.navigator.geolocation.getCurrentPosition(
function (position) {
deferred.resolve(position);
},
function (err) {
deferred.reject(err);
}
);
} else {
deferred.reject('Geolocation not supported.');
}
return deferred.promise;
}
};
}]).
factory('EventService', function($q, $http, ys$currentPosition) {
'use strict';
function fetchEvents() {
var deferred = $q.defer();
$http.get('/foos').then(function(result){
deferred.resolve(result.data);
});
return deferred.promise;
}
function appendDistanceFromEvents(events) {
var deferred = $q.defer();
ys$currentPosition.position().then(function(position){
_.forEach(events, function(event) {
event.distance = determineDistanceFromEvent(event, position);
});
deferred.resolve(events);
});
return deferred.promise;
}
function determineDistanceFromEvent(event, userPosition) {
var dist = getDistanceFromLatLonInMi(
userPosition.coords.latitude,
userPosition.coords.longitude,
event.latitude,
event.longitude
)
console.log(dist)
return dist;
}
var deg2rad = function(deg) {
return deg * (Math.PI/180)
}
var getDistanceFromLatLonInMi = function(lat1, lon1, lat2, lon2) {
var R = 6371; // Radius of the earth in km
var dLat = deg2rad(lat2-lat1); // deg2rad below
var dLon = deg2rad(lon2-lon1);
var a =
Math.sin(dLat/2) * Math.sin(dLat/2) +
Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) *
Math.sin(dLon/2) * Math.sin(dLon/2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
var d = R * c; // Distance in km
d = d * 0.621371;
return d;
};
return {
fetchEvents: fetchEvents,
appendDistanceFromEvents: appendDistanceFromEvents
};
});
angular.module('controllers', []).
controller('EventsController', ['$scope', '$controller', 'EventService', function($scope, $controller, EventService){
EventService.fetchEvents().then(function(events){
// real app is doing other stuff in here
// so would like to keep promise/then pattern
EventService.appendDistanceFromEvents(events).then(function(results) {
$scope.events = results;
});
});
}]);