Juan
Juan

Reputation: 2084

How to center ngx-leaflet map inside ng-bootstrap modal

I want to display a ngx-leaflet map inside a ng-bootstrap modal, centered in a determinate point. The map is encapsulated in a component like this:

HTML

<div class="modal-header">
  <h4 class="modal-title">Map</h4>
  <button type="button" class="close" aria-label="Close" 
      (click)="activeModal.dismiss('Cross click')">
      <span aria-hidden="true">&times;</span>
  </button>
</div>

<div class="modal-body">
  <div id="map" 
    leaflet
    [leafletOptions]="options"
    [leafletLayersControl]="layersControl"></div>
</div>

<div class="modal-footer">
  <button type="button" class="btn btn-outline-dark" (click)="activeModal.close('Close click')">Close</button>
</div>

TS

 constructor(public activeModal: NgbActiveModal) { }

  ngOnInit() {
  }

  streetMap = L.tileLayer('http://{s}.google.com/vt/lyrs=m&x={x}&y={y}&z={z}', {
    maxZoom: 20,
    subdomains: ['mt0', 'mt1', 'mt2', 'mt3']
  });

  hybridMap = L.tileLayer('http://{s}.google.com/vt/lyrs=s,h&x={x}&y={y}&z={z}', {
    maxZoom: 20,
    subdomains: ['mt0', 'mt1', 'mt2', 'mt3']
  });

  vehicleMarker = L.marker([40.4168, -3.703790], {
    icon: L.icon({
      iconSize: [25, 41],
      iconAnchor: [13, 41],
      iconUrl: 'assets/marker-icon.png',
      shadowUrl: 'assets/marker-shadow.png'
    })
  });

  layersControl = {
    baseLayers: {
      'Map view': this.streetMap,
      'Map hybrid': this.hybridMap
    },
    overlays: {
      'Vehicle': this.vehicleMarker
    }
  };

  options = {
    layers: [this.streetMap, this.vehicleMarker],
    zoom: 5,
    center: L.latLng([40.4168, -3.703790])
  };

And in another component I open the modal like this:

constructor(private modalService: NgbModal) { }

ngOnInit() {
}

openMap() {
    this.modalService.open(MapComponent);
}

Everything works fine outside the modal (the map is centered in the given point) but when I render the map inside the modal the map is not centered. How can I solve this issue?

I realice that in small screens (like Galaxy S5 360x460) the map is displayed fine (centered).

Upvotes: 2

Views: 2061

Answers (2)

Parham Dadgar
Parham Dadgar

Reputation: 21

I'm Parham, for fix this problem issue in Angular 6 and bootstrap Modal use this instruction. Today fixed my problem with this instruction. To correct this problem issue should be use method this.map.invalidateSize(); in event (leafletMouseOver). Please see this sample code from here down.

    /* // In style.css add this class map */
.map {
  position: relative !important;
  width: 100%; height: 600px;
  margin: 2px;
  padding: 2px;
}

/*--------------------------------------------------------------------*/

// In Angular component .ts file

import { Component, EventEmitter, OnInit, Output } from '@angular/core';
import * as L from 'leaflet';

@Component({
  selector: 'app-map-control',
  template: 
    `<div class="map" leaflet
      (leafletMapReady)="onMapReady($event)"
      [leafletOptions]="options"
      (leafletMouseOver)="refreshMap()"
      style="border: black solid 1px;">
    </div>`
})

export class MapControlComponent {

  constructor() { }

  public map: L.Map = null;
  private latoLong: L.LatLngTuple = [35.706000, 51.4025000]; // for set static lat-long to map


  // Define our base layers so we can reference them multiple times
  streetMaps = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
    detectRetina: true,
    attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
  });

  // Set the initial set of displayed layers (we could also use the leafletLayers input binding for this)
  options = {
    layers: [this.streetMaps],
    zoom: 17,
    center: L.latLng(this.latoLong)
  };

  @Output() outputLatLong = new EventEmitter<L.LatLngTuple>();

  refreshMap() {
    if (this.map) {
      // this.streetMaps.redraw();
      this.map.invalidateSize();
    }
  }

  onMapReady(map: L.Map) {
    map.on('click', (eventMouse: L.LeafletMouseEvent) => {
      this.latoLong = [eventMouse.latlng.lat, eventMouse.latlng.lng];
      map.setView(this.latoLong, map.getZoom());
      this.outputLatLong.emit(this.latoLong);
    });
    this.map = map;
  }

}

From using in your component application:

<app-map-control (outputLatLong)="getLatLong($event)"></app-map-control>

Upvotes: 1

reblace
reblace

Reputation: 4195

Leaflet is very sensitive to changes to page layout and sizing. Generally, when the page layout changes you need to call Map.invalidateSize().

ngx-leaflet automatically tracks window resize events and calls invalidateSize() for you, so you don't usually have to worry about it. But, when you're using something like Bootstrap or JQuery to manipulate the page layout independently of resizing the window, you may need to manually call it.

First, grab a reference to the Map using the instructions in the README: https://github.com/Asymmetrik/ngx-leaflet#getting-a-reference-to-the-map

<div leaflet
     [leafletOptions]="options"
     (leafletMapReady)="onMapReady($event)">
</div>

map: Map;
onMapReady(map: Map) {
   this.map = map;
}

Then, add a call to this.map.invalidateSize() whenever you open/show/resize the map modal.

If this doesn't help, please provide an angular CLI project on GitHub that reproduces the problem and I can take a closer look.

Upvotes: 3

Related Questions