Search code examples
angulartypescriptopenlayersopenstreetmap

angular: convert observable array to array to use data


I am trying to make data from my dataservice to show on an openstreetmap as pinpoints.

When I try this with 1 pinpoint using static data, it works and the pinpoint shows. Now I want to use my dataservice to get an array of Parking objects and use them in my map to create those pinpoints. However I the pinpoints do not show and when I console log the parking from the loop in my map component it only shows the 1st one and the array shows to be undefined when I log it in the console.

The issue is that I am trying to call a method from a dataservice which returns an observable of an array of Parking objects. Now I want to loop throug the observable array to get each individual parking object from which I want to extract its longtitude and latitude to make it show on the map

I'm new to angular so I think this might have to do with the array that I receive being an observable, but no clue as how I would need to convert it to a normal array, I've tried adding a .subscribe without any luck

code map

  map;
  testp;
  vectorSource;
  vectorLayer;
  rasterLayer;
  features: Feature[];
  constructor(
    private _pds: ParkingDataService
  ) { }

  ngOnInit(): void {
    this.createParkPoints();
    this.vectorSource = new VectorSource({
      features: this.features
    });

    this.vectorLayer = new VectorLayer({
      source: this.vectorSource
    });
    this.initializeMap();

  }

  createParkPoints(){
    this._pds.allParkings$.forEach( (parkings: Parking[]) =>{
      parkings.forEach((parking: Parking) => {
        console.log(parking);
        let ftre: Feature = new Feature({
          geometry: new Point(fromLonLat([parseFloat(parking.longtitude), parseFloat(parking.latitude)]))
        });

        ftre.setStyle(new Style({
          image: new Icon(({
            color: '#8959A8',
            crossOrigin: 'anonymous',
            src: 'assets/park.svg',
            imgSize: [30, 30]`enter code here`
          }))
        }));

        this.features.push(ftre);
      })
    })
  }

  initializeMap(){
    this.map = new Map({
      target: 'map',
      layers: [ new TileLayer({
        source: new OSM()
      }), this.vectorLayer ],
      view: new View({
        center: fromLonLat([3.7219431, 51.054633]),
        zoom: 15,
      })
    });
  }

}

code dataservice

export class ParkingDataService {
  private _parkings$ = new BehaviorSubject<Parking[]>([]);
  private _parkings: Parking [];

  constructor(private http: HttpClient) {
    this._parkings$
    .pipe(
      catchError(err => {
        this._parkings$.error(err);
        return throwError(err);
      })
    )
    .subscribe((parkings: Parking[]) => {
      this._parkings = parkings;
      this._parkings$.next(this._parkings);
    });
   }

  get allParkings$(): Observable<Parking[]>{
    return this.parkings$;
  }

  get parkings$(): Observable<Parking[]>{
    return this.http.get(`${environment.apiUrl}/Parking`).pipe(
      tap(console.log),
      shareReplay(1),
      catchError(this.handleError),
      map((list: any[]): Parking[] => list.map(Parking.fromJSON))
    );

  }

  get parkings(){
    this.parkings$.subscribe(parkings =>{
      this._parkings = parkings as Parking[];
    });
    console.log(this._parkings);
    return this._parkings;
  }

  getParking$(id: number): Observable<Parking>{
    return this.http
      .get(`${environment.apiUrl}/Parking/${id}`)
      .pipe(catchError(this.handleError), map(Parking.fromJSON));
  }

  handleError(err: any): Observable<never> {
    let errorMessage: string;
    if (err.error instanceof ErrorEvent) {
      errorMessage = `An error occurred: ${err.error.message}`;
    } else if (err instanceof HttpErrorResponse) {
      console.log(err);
      errorMessage = `'${err.status} ${err.statusText}' when accessing '${err.url}'`;
    } else {
      errorMessage = err;
    }
    return throwError(errorMessage);
  }
}

Solution

  • the service is returning an observable of type Parking[] (array of parkings)

    so, to get that array of parkings, we need to subscribe to that observable just in the component

    in the service we just need to return the obsrevable, and then subscribe to it in the component

    we need a simple function in the service to load all the parkings from the server, without the need of all that getters you use in the service

    so we can use some function like this in the service

    in this function we will return the observable

    loadAllParkings() {
        // here we will call the HTTP Request which returns an observable
        // you can omit this <Parking[]>, this refers to the return type of this http request, you can define this type in the map operator as you did before
    
        return this.http.get<Parking[]>(`${environment.apiUrl}/Parking`).pipe( // we are returning the observable here, so in the component we subscribe to this observable
            // do all the rxjs operators you need here
            // tap, map, catchError, ... and so on, as you need
        );
    }
    

    now we have an observable with type array of Parkings, so in the component, we just need to subscribe to that observable

    in the component

    createParkPoints () {
        // the function loadAllParkings in the service returns an observable, so we can do the subscription here
        this._pds.loadAllParkings().subscribe((parkings: Parking[]) => {
            // now you have the parkings array, loop over it
        });
    }