3

I'm currently trying to create a leaflet map inside an angular material2 tab-group as below

import {Component, NgModule} from '@angular/core'
import {BrowserModule} from '@angular/platform-browser'
import { MaterialModule } from '@angular/material';

import { Component, OnInit, OnDestroy } from '@angular/core';
import 'leaflet';

@Component({
  selector: 'minimap',
  template: `<div #minimap [id]="id" class=leaflet-map></div>`
})
export class MiniMap implements OnInit, OnDestroy {
  map: L.Map = null;
  id: string;

  constructor() { 
    this.id = "map" + Date.now();
  }

  ngOnInit() {
    this.map = L.map(this.id).setView([54.5, -115.0], 13);
    L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
      maxZoom: 18,
      attribution:
      '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
    }).addTo(this.map);
  }

  ngOnDestroy() { }
}

@Component({
  selector: 'my-app',
  template: `
    <div>
      <h2>Hello {{name}}</h2>
      <md-tab-group>
      <md-tab label="tab 1">
      tab 1 content
      </md-tab>
      <md-tab label="tab 2">
      tab 2 content
      </md-tab>
      <md-tab label="map tab">
      <minimap></minimap>
      </md-tab>
      </md-tab-group>
    </div>
  `,
})
export class App {
  name:string;
  constructor() {
    this.name = 'Angular2'
  }
}

https://plnkr.co/edit/dLwhv3XoMWMYFkqztkuR?p=preview

Unfortunately when it tries to create the map in the component it throws an error saying that the map container doesn't exist yet. my interpretation is that the code is currently running before the DOM object is created.

What is the correct way of creating the map such that the code is called when the DOM object exists??

Bjorn Harpe
  • 374
  • 4
  • 13

2 Answers2

6

Looks like referencing directly the map container Element (through @ViewChild) is working without issue.

import {Component, ViewChild} from '@angular/core';
import 'leaflet';

@Component({
    selector: 'minimap',
    template: `<div #mapDiv></div>`
})
export class MiniMap implements OnInit {
    @ViewChild('mapDiv') mapContainer;

    ngOnInit() {
        this.map = L.map(this.mapContainer.nativeElement);
    }
}

Updated Plunk: https://plnkr.co/edit/HGWb3J1f5HL8shFW9EUN?p=preview

However, it seems that you need to re-initialize the map size once the tab is revealed (at least the first time). See Data-toggle tab does not download Leaflet map

ghybs
  • 47,565
  • 6
  • 74
  • 99
  • 1
    This definitely works, however I don't quite understand it, Leaflet isn't able to find the element in the DOM by ID, yet it still exists and we can access it using viewChild?? is it just that the component DOM hasn't yet been inserted into the page and thats why leaflet can't find it? – Bjorn Harpe Feb 24 '17 at 08:28
  • 2
    @BjornHarpe: that is my guess indeed. Leaflet tries to [find the element in `document`](https://github.com/Leaflet/Leaflet/blob/master/src/dom/DomUtil.js#L38-L40). – ghybs Feb 24 '17 at 09:32
1

Try reading the documentation for the lifecycle hook:

https://angular.io/docs/ts/latest/guide/lifecycle-hooks.html

Camilo
  • 11
  • 2
  • I forgot to mention that I have tried both the `AfterViewInit` and `AfterContentInit` and observed the same behaviour, is there another point I should be trying to hook? – Bjorn Harpe Feb 24 '17 at 00:22