Reputation: 199
I have a service to monitor device position:
getLocation(opts): Observable<any> {
return Observable.create(observer => {
if (window.navigator && window.navigator.geolocation) {
window.navigator.geolocation.watchPosition((position) => {
observer.next(position);
}, (error) => {
switch (error.code) {
case 1:
observer.error('errors.location.permissionDenied');
break;
case 2:
observer.error('errors.location.positionUnavailable');
break;
case 3:
observer.error('errors.location.timeout');
break;
}
}, opts);
} else {
observer.error('errors.location.unsupportedBrowser');
}
});
}
then retrieve lat,long in component:
ngOnInit() {
var source = this.locationService.getLocation({enableHighAccuracy:true, maximumAge:30000, timeout:27000});
source.subscribe(pos => {
this.lat = pos.coords.latitude;
this.long = pos.coords.longitude;
}, err => {
this.err = err;
console.log(err);
});
}
this code work fine in browser on macbook and iphone i.e. it can retrieve and update position when device move.
but on my ipad(wifi only no gps) it can get position at first time then few second later, service return error code 2 i.e. position unavailable and browser stop update position. i'm not sure it's stop working or it's still running but alway return error code2.
My questions are:
Upvotes: 2
Views: 3451
Reputation: 383
Here I am using the watchPosition
function to get location from the user. Create a subject with a default location (optional). When the component loads, call getLocation
and within your component, use an async pipe to display the data.
<div *ngIf="coordindates | async">
<p>
{{coordindates | async | json}}
</p>
</div>
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import {
LocationService
} from '@app/core';
import { Observable } from 'rxjs';
@Component({
selector: 'app-location',
templateUrl: './location.component.html',
styleUrls: ['./location.component.scss']
})
export class LocationComponent implements OnInit {
coordindates: Observable<Object>;
constructor(private locationService: LocationService) { }
ngOnInit(): void {
this.coordindates = this.locationService.getLocation();
}
}
import { Injectable } from '@angular/core';
import { BehaviorSubject, Observable, Subject } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class LocationService {
public watchId: number;
public locationTrackingActive = false;
public currentLoation: { latitude: number, longitude: number } = { latitude: undefined, longitude: undefined };
private reactiveDeviceLocation$: Subject<Object>;
constructor() {
this.reactiveDeviceLocation$ = new BehaviorSubject<Object>(this.currentLoation);
}
getLocation(): Observable<Object> {
const opts = { enableHighAccuracy: true, maximumAge: 60000, timeout: 30000 };
this.watchId = navigator.geolocation.watchPosition((position) => {
this.locationTrackingActive = true;
this.currentLoation = { latitude: position.coords.latitude, longitude: position.coords.longitude };
this.reactiveDeviceLocation$.next(this.currentLoation);
},
(err) => {
console.log(err);
this.locationTrackingActive = false;
},
opts);
return this.reactiveDeviceLocation$;
}
}
Upvotes: 2
Reputation: 106
There's a weird bug that I encountered in certain browsers using navigator.geolocation.watchPosition in an Angular2 service with RxJS, Meteor, and a bunch of other stuff. Not sure what the cause was, but "this" goes out of scope after the first time the callback is made and cannot be addressed in future callbacks. The fix is to create a locally scoped reference to the current class:
startTracking() {
let that = this; //The fix
let opts = {maximumAge: 60000, timeout: 30000}
this.watchId = navigator.geolocation.watchPosition((position) =>
{
console.log(position);
that.locationTrackingActive = true;
that.reactiveDeviceLocation.next(position.coords);
},
(err) =>
{
console.log(err);
that.locationTrackingActive = false;
},
opts);
}
You do not need to resubscribe to watchPosition after an error, it keeps trying until it's able to get a signal. For your observable, using a BehaviorSubject (reactiveDeviceLocation above) lets you set a default starting location, then update it only on success... this way when a new client subscribes, they will be given the default (or most recent) location and can can work with that until watchPosition succeeds in updating the subject.
I set it up as follows (in the same service)
reactiveDeviceLocation: Rx.BehaviorSubject<Object> = new Rx.BehaviorSubject<Object>({latitude:37.5, longitude:122});
getReactiveDeviceLocation() {
return this.reactiveDeviceLocation;
}
Any consumer of the service can then call
MyService.getReactiveDeviceLocation.subscribe((pos) => {
console.log(pos); //Last known location.
})
Upvotes: 0