Topic: leaflet map doesn't render properly in stepper

ATLAS IoT Tech GmbH free asked 3 years ago


I've integrated different leaflet-maps from ngx-leaflet successfully in my angular project. Now I've got the following problem with the mdb-stepper. I need to render the leaflet map inside a step of the stepper. But the stepper doesn't load properly in css and functionality.

I think this is because the steps of the stepper change their size when going from one to another step. But I just don't know how to fix this. I tried rendering the map & set the size again, when the related step is active, but this doesn't work. Also a common solution with the function invalidateSize(); from the leaflet map doesn't work for me. I use the horizontal stepper.

Can you please help me?

My HTML:

 <mdb-step #standortStep name="Standort">
    <ng-container *ngIf="standortStep.isActive">
        <div class="col-12 col-md-9">
            <div class="row m-0 mt-4">
                <mdb-card class="location-title p-2 w-100">
                    Standort
                </mdb-card>
            </div>
            <div class="row m-0 px-2">
                <div class="col-12 col-md-8 pl-0 mt-4">
                    <div class="create-map-wrapper">
                        <div leaflet class="create-map" [leafletCenter]="center"
                            [leafletFitBounds]="fitBounds" [leafletBaseLayers]="baseLayers"
                            [leafletLayers]="markers" (leafletClick)="mapClicked($event)"
                            *ngIf="standortStep.isActive">
                        </div>
                    </div>
                </div>                
            </div>
        </div>
    </ng-container>
</mdb-step>

My CSS:

.create-map-wrapper {
  width: 100%;
  .create-map {
    height: 30vh;
  }

This is one thing I tried:

onMapReady(map: L.Map) {
        this.map = map;
        f(this.map !== undefined) {
                    setTimeout(() => {
                        this.map.invalidateSize();
                    }, 10);
                }
    }

Arkadiusz Idzikowski staff commented 3 years ago

Please provide some more HTML/TS code so we can reproduce this problem on our end. Which version of stepper do you use? Horizontal or vertical?


Arkadiusz Idzikowski staff answered 3 years ago


The solution with *ngIf should resolve the problem. You need to hide the map when switching to another step and show it again to reinvoke the (leafletMapReady) event. You may also need to wrap the code in the onMapReady method in setTimeout.

Here is an example:

HTML:

<mdb-stepper #stepper (stepChange)="onStepChange($event)">
    <mdb-step name="Step 1" [stepForm]="firstFormGroup">
      <button mdbBtn size="sm" color="primary" (click)="stepper.next()">CONTINUE</button>
    </mdb-step>
    <mdb-step name="Step 2" [stepForm]="secondFormGroup">
        <div *ngIf="showMap" style="height: 300px" leaflet [leafletOptions]="leafletOptions" (leafletMapReady)="onMapReady($event)"></div>
      <button mdbBtn size="sm" color="primary" (click)="stepper.next()">CONTINUE</button>
      <button mdbBtn size="sm" color="secondary" (click)="stepper.previous()">BACK</button>
    </mdb-step>
    <mdb-step name="Step 3" label="Step 3 label">
      <p class="pl-2">Finish!</p>
      <div class="step-actions">
        <button mdbBtn  size="sm" color="primary" (click)="onSubmit()">SUBMIT</button>
      </div>
    </mdb-step>
  </mdb-stepper>

TS:

import { Component, OnInit } from '@angular/core';
import { FormGroup, FormControl, Validators } from '@angular/forms';
import { latLng, tileLayer } from 'leaflet';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss'],
})
export class AppComponent implements OnInit {
  firstFormGroup: FormGroup;
  secondFormGroup: FormGroup;

  showMap = false;

  leafletOptions = {
    layers: [
      tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { maxZoom: 18, attribution: '...' })
    ],
    zoom: 5,
    center: latLng(46.879966, -121.726909)
  };

  ngOnInit() {
    this.firstFormGroup = new FormGroup({
      email: new FormControl('', [Validators.required, Validators.email])
    });
    this.secondFormGroup = new FormGroup({
      password: new FormControl('', Validators.required)
    });
  }

  get email() { return this.firstFormGroup.get('email'); }
  get password() { return this.secondFormGroup.get('password'); }

  onSubmit() {
    // do something here
  }

  onMapReady(map: any) {
    setTimeout(() => {
      map.invalidateSize();
    }, 0);
  }

  onStepChange(event: any) {
    if (event.activeStepIndex === 1) {
      this.showMap = true;
    }
  }
}


Please insert min. 20 characters.

FREE CONSULTATION

Hire our experts to build a dedicated project. We'll analyze your business requirements, for free.

Status

Answered

Specification of the issue

  • ForumUser: Free
  • Premium support: No
  • Technology: MDB Angular
  • MDB Version: 9.3.1
  • Device: PC
  • Browser: chrome
  • OS: Windows 10
  • Provided sample code: No
  • Provided link: No