Search code examples
javascriptgoogle-mapsionic-frameworkservicemodal-dialog

Ionic 4/5 - google maps in modal window with a service


I am showing maps in Ionic 5 with the javascript sdk. I load these maps with a service created for that.

Everything is fine when I do it in pages but if I show one of those pages as modal, the map does not load and the marker places it on the page from which the modal window was opened instead of in the modal window itself.

Let's see if I can put the code below so that it is not too "heavy"

home.page.html

<ion-header [translucent]="true">
  <ion-toolbar>
    <ion-title>
      HOME
    </ion-title>
  </ion-toolbar>
</ion-header>

<ion-content [fullscreen]="true">
    <div id="map"></div>
    <ion-button href = "page2" expand = "block">page 2</ion-button>
    <ion-button (click)="presentModal()" expand = "block" >page 3 modal</ion-button>
</ion-content>

page2.page.html

<ion-header>
  <ion-toolbar>
    <ion-title>page2</ion-title>
  </ion-toolbar>
</ion-header>

<ion-content>
    <div id="map"></div>
    <ion-button href = "home" expand = "block" >home</ion-button>
</ion-content>

page3.page.html

<ion-header>
  <ion-toolbar>
    <ion-title>page3</ion-title>
  </ion-toolbar>
</ion-header>
<ion-content>
    <div id="map"></div>
    <ion-button href = "home" expand = "block" >home</ion-button>
</ion-content>

home.page.ts

import { Component, OnInit }            from '@angular/core';
import { ModalController }              from '@ionic/angular';
import { LocationService }          from 'src/app/services/Location.service'
import { Page3Page }                    from 'src/app/pages/page3/page3.page';

@Component({
  selector: 'app-home',
  templateUrl: 'home.page.html',
  styleUrls: ['home.page.scss'],
})
export class HomePage {
    constructor(public LocationService: LocationService,
                public ModalController: ModalController) {}

    ngOnInit () {
        this.LocationService.loadMap("home");
        }

    async presentModal() {
        const modal = await this.ModalController.create({
            component: Page3Page
            });
        return await modal.present();
        }
}

page2.page.ts

import { Component, OnInit } from '@angular/core';
import { LocationService }      from 'src/app/services/Location.service'

@Component({
    selector: 'app-page2',
    templateUrl: './page2.page.html',
    styleUrls: ['./page2.page.scss'],
    })
export class Page2Page implements OnInit {
    constructor(private LocationService: LocationService) {}

    ngOnInit () {
        //this.loadMap();
        this.LocationService.loadMap("page2");
        }
}

page3.page.ts

import { Component, OnInit } from '@angular/core';
import { LocationService }      from 'src/app/services/Location.service'

@Component({
  selector: 'app-page3',
  templateUrl: './page3.page.html',
  styleUrls: ['./page3.page.scss'],
})
export class Page3Page implements OnInit {
    constructor(private LocationService: LocationService) {}

    ngOnInit() {
        this.LocationService.loadMap("page3");
        }
}

location.service.ts

import { Injectable } from '@angular/core';

declare var google;

interface Marker {
    position: {
      lat: number,
      lng: number,
    };
    title: string;
  }

@Injectable({
  providedIn: 'root'
})
export class LocationService {

    map = null;

    constructor() { }

    loadMap(prmtrMapId) {
        // create a new map by passing HTMLElement
        const mapEle: HTMLElement = document.getElementById('map');
        var myLatLng, myTitle;
        var marker;
        // create LatLng object
        switch (prmtrMapId) {
            case "home": // No hay sentencia "break" en el 'case 0:', por lo tanto este caso también será ejecutado
                myLatLng = {lat: 54.1675272, lng: -7.5138625}; //england
                myTitle = "home -> England";
                break; // Al encontrar un "break", no será ejecutado el 'case 2:'
            case "page2":
                myLatLng = {lat: 51.713614, lng: 10.956151}; //deutschland 
                myTitle = "page2 -> Deutschland ";
                break;
            case "page3":
                myLatLng = {lat: 47.820012, lng: 3.490945}; //france
                myTitle = "page3 modal -> France";
                break;
            default:
                myLatLng = {lat: 4.658383846282959, lng: -74.09394073486328};
          }
       
        marker = {
            position: myLatLng,
            title: myTitle
            };

        // create map
        this.map = new google.maps.Map(mapEle, {
            center: marker.position,
            zoom: 4
            });

        google.maps.event.addListenerOnce(this.map, 'idle', () => {
            this.addMarker(marker);
        });
    }

    addMarker(marker: Marker) {
    return new google.maps.Marker({
        position: marker.position,
        map: this.map,
        title: marker.title
    });
    }
}

Any help is apreciated !

thanks a lot

Ernesto


Solution

  • You can use the elementRef and pass it along like this:

    Change the service to this:

    loadMap(prmtrMapId, elRef: ElementRef) {
       const mapEle: HTMLElement = elRef.nativeElement;
    }
    

    In Page3 change the html to

    <ion-content>
        <div #map></div>
        <ion-button href = "home" expand = "block" >home</ion-button>
    </ion-content>
    

    and in the ts add the ViewChild and call the service like this

    @ViewChild('map') mapEl: ElementRef;
    
    ngAfterViewInit(){
       this.LocationService.loadMap("page3", this.mapEl);
    }
    

    Because "View queries are set before the ngAfterViewInit callback is called." You need to use ngAfterViewInit or static: true.

    https://angular.io/api/core/ViewChild#description