typescript - OpenLayers 5 | Angular 7 | Angular Material | Harta nu funcționează în interiorul angular material mat-stepper


Am importat harta OpenLayers în fișierul meu ts al componentului, apoi am creat un bloc div cu id=map unde trebuie să fie afișată harta OpenLayers, dar nu se afișează. Când am mutat blocul div (#map) în afara blocurilor mat-stepper, atunci funcționează.

Component.ts:

import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';

import OlMap from 'ol/Map';
import OlXYZ from 'ol/source/XYZ';
import OlTileLayer from 'ol/layer/Tile';
import OlView from 'ol/View';
import { fromLonLat } from 'ol/proj';

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

  map: OlMap;
  source: OlXYZ;
  layer: OlTileLayer;
  view: OlView;

  ...

  ngOnInit() {
    this.source = new OlXYZ({
      url: 'http://tile.osm.org/{z}/{x}/{y}.png',
    });

    this.layer = new OlTileLayer({
      source: this.source,
    });

    this.view = new OlView({
      center: fromLonLat([6.661594, 50.433237]),
      zoom: 3,
    });

    this.map = new OlMap({
      target: 'map',
      layers: [this.layer],
      view: this.view,
    });
  }

Component.html:

<mat-horizontal-stepper
  linear
  labelPosition="bottom"
  #stepper
  class="sell-form-container"
>
  <mat-step ...>
    <form ...>
      ...
      <div id="map"></div>  //<- here it does not work, map does not display
      ...
    </form>
  </mat-step>
  ...
</mat-horizontal-stepper>
<div id="map"></div> //<- here it works, map displays

Component.css:

#map {
  width: 100%;
  height: 500px;
}

Ce este greșit în codul meu?

Răspuns #1

Cred că problema ta este legată de faptul că conținutul la care încerci să atașezi OLMap se află în conținutul transclus al mat-stepper sau mat-step.

În ciclul de viață al componentelor, componenta copil nu este pregătită la pasul OnInit al părintelui. Deoarece ai pus div-ul de care ai nevoie în interiorul componentei mat, acesta este inclus în schimb în ciclul de viață al acelei componente.

O modalitate prin care ar trebui să poți rezolva acest lucru este lucrând cu metoda de ciclu de viață ngAfterViewInit. Având codul tău în acea metodă ar trebui să rezolve această problemă.

ngAfterViewInit() {
  this.map = new OlMap({
    target: 'map',
    layers: [this.layer],
    view: this.view,
  });
}

Aș dori să prezint și un alt mod de a lucra cu componentele, pentru a satisface eventualele curiozități. Ținând cont de conținutul transcluzat, dacă elementul pe care îl dorești se află în componenta curentă, ar trebui să folosești decoratorul ViewChild pentru a-l selecta. În caz contrar, dacă se află într-o componentă copil, folosește decoratorul ContentChild.

// html
<mat-horizontal-stepper ...>
  <mat-step ...>
    <form ...>
      ...
      <div #myMapRef id="map"></div>
      ...
    </form>
  </mat-step>
  ...
</mat-horizontal-stepper>

// parent ts
@ContentChild('myMapRef') // <-- if it's in a child component, your case should use this
@ViewChild('myMapRef') // <-- if it's in this component, your "this is working" would use this
myMap: ElementRef;

ngAfterViewInit() {
  console.log(this.myMap); // can get the ID from the ElementRef
}

Recomand cu tărie să te familiarizezi cu adevărat cu profunzimile ciclului de viață al componentelor. Acesta este ceva ce aș fi vrut să fac mai devreme în timp ce învățam Angular.

  • Metodele ciclului de viață Angular
  • Angular ContentChild
  • Angular ViewChild
  • Un exemplu lung de postare pe blog
  • După exemplul de conținut
  • După vizualizarea exemplului