Search code examples
javascriptangulartypescriptleafletpopup

Angular/Leaflet/OpenStreetMap - clicking on pin for pop-up throws error about an already defined service variable, doesn't display binded pop-up


The error appears in the browser, when I click on any pin. No pop-up is displayed, nothing happens, just the error in the console.

enter image description here

Below you will find the component that displays the map and the pins and it should display the pop-up. The service is injected in the constructor.

The same service variable is successfully used when populating the map with pins.

import { Component, OnInit } from '@angular/core';
import * as L from 'leaflet';
import { PowerPlantService } from 'src/app/services/power-plant.service';
import { PowerPlant } from 'src/app/models/power-plant.model';
import { Observable } from 'rxjs';

@Component({
  selector: 'app-home',
  templateUrl: './home.component.html',
  styleUrls: ['./home.component.css']
})
export class HomeComponent implements OnInit {

    //coordinates for Brasov
  private latitude: number = 45.6427;
  private longitude: number = 25.5887;
  
  marker!: CustomPinMarker ;
  
  private map!: L.Map;
  private centroid: L.LatLngExpression = [this.latitude, this.longitude];
  powerPlantList!: PowerPlant[];

  ngOnInit(): void {
    this.initMap();
  }
  
  constructor(private powerPlantService: PowerPlantService) {

  }

  private initMap(): void {
    this.map = L.map('map', {
      center: this.centroid,
      zoom: 2.8
    });

    const tiles = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', 
    {
      minZoom: 2.8,
      attribution: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'
    });

    tiles.addTo(this.map);
  
     /*
    the get request made through the HttpClient call will generate an Observable type to which we have to subscribe and unsubscribe at the end.
    */

    this.powerPlantService.getAll().subscribe(powerPlantArrayFromGet => {
      console.log(powerPlantArrayFromGet);
      this.powerPlantList = powerPlantArrayFromGet;
      this.powerPlantList.forEach( (element) => {
          if (element.published) {
            this.marker = new CustomPinMarker([element.latitude!, element.longitude!], element.gppd_idnr).on('click', this.onPinClick).addTo(this.map);
                       
          }          
      });
    })
  }

  onPinClick() : void{
       console.log("In pinclick method");
       let observableVar! : Observable<PowerPlant>;
       let powerPlant! : PowerPlant;
       observableVar = this.powerPlantService.findByGPPD_INDR(this.marker.getId());

       observableVar.subscribe(data => {powerPlant = data});
       this.marker.bindPopup(""+ powerPlant.powerPlant_name + '<br\>' + powerPlant.country_name + '<br\>' + powerPlant.est_generation_gwh_2017
      + '<br\>' + powerPlant.primaryFuel ).openPopup();
  }
}

export class CustomPinMarker extends L.Marker {
   gppd_idnr: String | undefined;

  constructor(latLng: L.LatLngExpression, gppd_idnr: string | String | undefined, options?: L.MarkerOptions) {
    super(latLng, options);
    this.gppd_idnr = gppd_idnr;
  }

  getId() : any{
    return this.gppd_idnr;
  }
}

Solution

  • This is the correct answer after some modifications:

    import { Component, OnInit } from '@angular/core';
    import * as L from 'leaflet';
    import { PowerPlantService } from 'src/app/services/power-plant.service';
    import { PowerPlant } from 'src/app/models/power-plant.model';
    import { Observable } from 'rxjs';
    
    @Component({
      selector: 'app-home',
      templateUrl: './home.component.html',
      styleUrls: ['./home.component.css']
    })
    export class HomeComponent implements OnInit {
     //coordinates for Brasov
      private latitude: number = 45.6427;
      private longitude: number = 25.5887;
       
      private map!: L.Map;
      private centroid: L.LatLngExpression = [this.latitude, this.longitude];
      powerPlantList!: PowerPlant[];
    
      ngOnInit(): void {
        this.initMap();
      }
      
      constructor(private powerPlantService: PowerPlantService) {  }
    
      private initMap(): void {
        this.map = L.map('map', {
          center: this.centroid,
          zoom: 2.8
        });
    
        const tiles = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', 
        {
          minZoom: 2.8,
          attribution: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'
        });
    
        tiles.addTo(this.map);
      
        // this queries the database and retrieves a list of all the elements
        this.powerPlantService.getAll().subscribe(powerPlantArrayFromGet => {
          console.log(powerPlantArrayFromGet);
          this.powerPlantList = powerPlantArrayFromGet;
       // this loops through the list and creates and places a pin on the map at the location of each element
          this.powerPlantList.forEach( (element) => {
              if (element.published) {
                const that = this;
                //console.log(element.gppd_idnr + " 1")  ;  
                // this create the pin to be placed on the map and attaches to it a pop-up that, when clicked, will retrieve from the database information about that item and display it on the pop-up      
                let marker = new L.Marker([element.latitude!, element.longitude!])
                  .on('click', function() {
                    let observableVar! : Observable<PowerPlant>;
                    let powerPlant! : PowerPlant;
                    //console.log(element.gppd_idnr + " 2");
                    observableVar = that.powerPlantService.findByGPPD_INDR(element.gppd_idnr + "");
                // the is where the interogation is done (above) and the pop-up is populated with the relevant information
                    observableVar.subscribe(
                      data => 
                      {
                        powerPlant = data
                
                        var latlong : L.LatLng = new L.LatLng(<number> powerPlant.latitude, <number> powerPlant.longitude);
    
                        marker.setLatLng(latlong).bindPopup( 
                          powerPlant.powerPlant_name + 
                          '<br\>' + 
                          powerPlant.country_name + 
                          '<br\>' + 
                          powerPlant.est_generation_gwh_2017 + 
                          '<br\>' + 
                          powerPlant.primaryFuel )
                          .openPopup();
                      })
                  })
                  .addTo(that.map);                       
              }          
          });
        })
      }
    }