Angular2 + Google Maps API Idle event listener issue

1.9k views Asked by At

I'm having this issue with Angular2 + GMaps API. I want to show a spinner preloader while the map loads and set some markers. When using the addListenerOnce IDLE Listener, at the first call the listener takes control of my scope, my variable "this" in my component turns into the map itself, so when I want to execute some function or callback inside the listener, for example, if I write something like:

public isLoaded: boolean = false;
...
google.maps.event.addListener(map, 'idle', () => {
this.isLoaded= true;
});

the variable THIS into the listener (thats should reference to the Component scope) turns into the google map itself, hence this.isLoaded returns Undefined (isLoaded is a property in my Component not map property). It's like the listener takes control of my component scope,

the strange thing is that this occurs once, when the component loads the first time, then it fixes and everything works just fine.

Any solution? Sorry for my english. Thanks in advance!

3

There are 3 answers

6
Valikhan Akhmedov On

In your snippet context of function execution sets explicitly by Gmaps like this callback.apply(this, args)

So for your case you just need to also set context explicitly, for this purpose Function has .bind() method (https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_objects/Function/bind)

public isLoaded: boolean = false;
...

const myCallback = () => {
this.isLoaded= true;
};

google.maps.event.addListener(map, 'idle',myCallback.bind(this));
0
Nicolás On

It still doesn't work. Well, I think I kind of solve the scope issue by adding .bind(this) to my callback like this:

 console.log("THIS OUTSIDE listener callback:", this)
        google.maps.event.addListenerOnce(this.map, 'idle', function () {
            this.loaded = true;
            console.log("THIS INSIDE listener callback:", this)
        }.bind(this));

The console output is:

"THIS OUTSIDE listener callback: MapaComponent {loaded: false, latitude: 31.7413, longitude: -60.5115, bounds: _.ae, zoom: 12…}"
"THIS INSIDE listener callback: MapaComponent {loaded: true, latitude: -31.7413, longitude: -60.5115, bounds: _.ae, zoom: 12…}"

So... i guess the scope issue is solved. However my spinner is NOT hidding and my Map cannot be seen when the page is loaded. If I navigate to another page inside my app and then go back to my map page, then the spinner hides and everything works fine. My component code (summarized) is:

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

@Component({
    selector: 'mapa-global',
    template: `
    <div class="container" [hidden]="loaded">
        <div class="preloader-wrapper active" id="loader">
            <div class="spinner-layer spinner-red-only">
            <div class="circle-clipper left">
                <div class="circle"></div>
            </div><div class="gap-patch">
                <div class="circle"></div>
            </div><div class="circle-clipper right">
                <div class="circle"></div>
                </div>
            </div>
        </div>   
      </div>

    <div id="map-canvas-container" [hidden]="!loaded">
        <div id="map-canvas"></div>   
    </div>
    `,
    styleUrls: ['./mapa.component.css']
})
export class MapaComponent implements OnInit {

    public loaded: boolean = false;
    private latitude: number = -31.7413;
    private longitude: number = -60.5115;
    private bounds: any = new google.maps.LatLngBounds();
    private zoom: number = 12;
    map: any;

    ngOnInit() { this.initMap(); }

    initMap() {
        this.map = new google.maps.Map(document.getElementById('map-canvas'), {
            center: { lat: this.latitude, lng: this.longitude },
            zoom: this.zoom
        });

        console.log("THIS OUTSIDE listener callback:", this)
        google.maps.event.addListenerOnce(this.map, 'idle', function () {
            this.loaded = true;
            console.log("THIS INSIDE listener callback:", this)
        }.bind(this));
    }
}

Any suggestions? Maybe the [hidden] directive is implemented in a wrong way?

0
Nicolás On

Thanks to Valikhan Akhmedov my GMap preloader is working! Final solution:

...

constructor(private _detector: ChangeDetectorRef){}

    ngOnInit() { this.initMap(); }

    initMap() {
        this.map = new google.maps.Map(document.getElementById('map-canvas'), {
            center: { lat: this.latitude, lng: this.longitude },
            zoom: this.zoom
        });

        google.maps.event.addListenerOnce(this.map, 'idle', function () {
            this.loaded = true;
            this._detector.detectChanges();
            google.maps.event.trigger(this.map,'resize');
            this.map.setCenter({ lat: this.latitude, lng: this.longitude });         
        }.bind(this));
    }