Stimulus Discourse

Call Stimulus Function from Google Maps callback

Hi There,

Is there a way to call a stimulus function from the google maps url callback?

Something like:

https://maps.googleapis.com/maps/api/js?key={{env(‘GOOGLE_PLACES_API_KEY’)}}&libraries=places&callback=address#initMap”

In this case there is an initMap function in the address_controller.

Alternatively, I could just check that the API has loaded in the connect function and then call initMap, but just wanted to see if there was a way to utilize the URL callback.

I’ll be doing a fair amount of Stimulus/Google Maps, so I’m happy to share what I learn if anyone has questions.

Thanks for any help!

1 Like

The best way to do this right now is to turn the Google Maps callback into a custom event, and then connect a controller action to listen for that event.

Something like this:

https://maps.googleapis.com/maps/api/js?...&callback=dispatchMapsEvent
window.dispatchMapsEvent = function(...args) {
  const event = document.createEvent("Events")
  event.initEvent("google-maps-callback", true, true)
  event.args = args
  window.dispatchEvent(event)
}
<div data-controller="address"
     data-action="google-maps-callback@window->address#initMap">
3 Likes

Wow. I had temporarily triggered a click on a hidden element.

This feels cleaner. Thank you!

Actually this won’t work because in my case event triggers faster than stimulus controller connects

So what I did is
in application.ts

import GoogleMapsCallback from "../util/google_maps_callback_helper";
let promiseResolver: ()=> void;
const promise: Promise<boolean> = new Promise((res) => {
promiseResolver = res;
});

(window as any).googleMapCallback = () => {
promiseResolver();
};

GoogleMapsCallback.init(promise);

Then in helper file google_maps_callback_helper

export default class GoogleMapsCallback {
    private static promise: Promise<boolean>;
    static init(promise: Promise<boolean>) {
        this.promise = promise;
    }

    static ready() {
        return this.promise;
    }
}

Then in map controller

import { Controller } from "stimulus";
import GoogleMapsCallback from "../util/google_maps_callback_helper";

export default class extends Controller {
static targets = ["canvas"];

private canvasTarget: HTMLDivElement;

connect() {
    GoogleMapsCallback.ready().then(() => {
        this.initGoogleMap();
    }).catch(() => {
        console.error("Google maps have not been loaded");
    });
}

initGoogleMap() {

With this I have promise waiting for window.googleMapCallback to be called and this promise is stored in my google_maps_callback_helper class, which gives actual state of promise to map controller. So in case where map loaded before controller I have in connect already resolved promise, otherwise promise will be resolved later on. Cheers