简体   繁体   中英

Openlayers map in Angular is blank when switching components

I have a MapComponent which loads an OpenLayers Map. This component is re-used in various of my Angular app's pages.

The map loads fine the first time. However, when I navigate to another component that contains the map, the map goes blank. In order to see it again, I have to navigate to a component that does not include the map, then navigate back to the desired page.

I only observe this behaviour since upgrading to Angular 10 (from 8).

Openlayers version 6.5 and 5.3 both exhibit this problem.

Attempts to solve:

  1. I've tried forcibly setting this.map = null in an OnDestroy lifecycle hook of my pages. Does not sound like a good solution and does not work .

  2. I've tried throwing an error in AfterContentInit:

ngAfterContentInit() {
   throw new Error("gramschaap");
}

It messes a bit with the rendering of the page and of course leaves a big fat error, but works .

  1. I've tried setting a timeout that removes and re-sets the target of the map in ngAfterViewInit :
ngAfterViewInit() {
    this.initMap();
    setTimeout(() => {
      if (this.map) {
        this.map.setTarget(null);
        this.map.setTarget("mapTarget");
      }
    }, 1000);
}

This of course means the map is blank for a second before it is redrawn; but it works!

  1. I've tried setting the map target to something non-existing at ngOnDestroy .
ngOnDestroy() {
    this.map.setTarget("raargitaar");  // does not exist
}

I thought it may have something to do with the target not initialising a second time. But it does not work .

Another approach could be to use dynamic map ids as a target.

I eliminated my problem similar to your third attempt. However, I gave 0 timeout rather than giving 1 second and you don't need setTarget(null) .

private initMap(): void {
  this.map = new Map({...})
  setTimeout(() => {
    this.map.setTarget('id');
  }, 0);
}

I was baffled by this issue and tried many things which didn't work. Strangely, if you place the following piece of HTML outside of your main container, map is rendered however, if you need it to be placed inside your container then the map is not rendered.

<div id="map"></div>

It seems that following approach works although it adds 1 second of delay to loading the map but I think that should be tolerable.

  ngAfterViewInit() {
    this.map = new Map({
      layers: [
        new TileLayer({
          source: new OSM()
        })
      ],
      view: new View({
        center: [36.85119033, -1.29393749], /* Coordinates */
        zoom: 5
      })
    });
    setTimeout(() => {
      if (this.map) {
        this.map.setTarget("map");
      }
    }, 1000);
  }

I don't see the original code. But here's what I use:

I have a dropdown list which switches between two map types. I need to redraw corresponding map type, ie render the proper map.

I use BehaviorSubject as a source for the dropdown list:

mapSource$: BehaviorSubject<'google' | 'openlayers'> = new BehaviorSubject<'google' | 'openlayers'>('google');

And I react on its value change in the event of my component which encapsulates both OpenLayers and Google map (one of these is active at a time):

  ngAfterViewInit() {
    this.mapSource$.subscribe((mapSource: 'google' | 'openlayers') => {
      this.cd.detectChanges();
      this.initMap();
    });
  }

Adding timeout to map.setTarget() is the best solution for me. For the second time, you should renew the map variable by this.map = new Map({Option}) .

this.map = new Map(mapOptions);

setTimeout(() => {
  this.map.setTarget(MAP_TARGET);
  this.onMapReady$.next();
}, 100);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM