angular-google-maps icon indicating copy to clipboard operation
angular-google-maps copied to clipboard

Allow reloading Google Maps API on network errors

Open 0xDCA opened this issue 8 years ago • 9 comments

This is similar to https://github.com/angular-ui/angular-google-maps/issues/1520, https://github.com/angular-ui/angular-google-maps/issues/1604 , and there is a PR in https://github.com/angular-ui/angular-google-maps/pull/1567, but I believe the PR solution would not be enough for all cases.

I am using angular-google-maps in a cordova + ionic project, and sometimes, even though the device is connected to my local network (i.e, online), the Maps Javascript API is not downloaded correctly (probably because my internet connection fails sometimes). In general, this could happen if the device gets online and then offline really fast (so the Maps API request is triggered but fails), or simply because the user is behind a proxy that blocks requests to the Maps API url.

One really simple solution is allow reloading the Maps API. I am trying to do that from my application by removing and re-adding the Maps script tag (with the same callback, so the uiGmapGoogleMapApi promise is resolved transparently), but it will fail if this reload is triggered after a previous load succeds but the Maps API is not executed yet (as this will cause the API to be executed twice, which raises an exception). As the only way (I can think of) we have to detect such conditions is using the onload and onerror callbacks from the script tag, this needs to be done from angular-google-maps (otherwise it could still happen after the first load).

So, what I am proposing is:

  1. Add onerror and onload handlers to the script tag added here, to detect when a previous request is in progress or failed
  2. Add a new service or extend uiGmapMapScriptLoader with a public reload method that removes and re-adds the script tag with the same callback as the previous one (so the uiGmapGoogleMapApi promise is resolved and not stalling forever), when no other load is in progress and the previous load was not successful (to prevent loading Google Maps twice, which would trigger an exception)
  3. Maybe adding a public property/getter to know if the Maps was already loaded or is loading (to enable apps to notify users when this happens)

This would allow Google Maps to be loaded at any time and it would be transparent to API users, as the uiGmapGoogleMapApi promise would be resolved in the same way it is resolved right now.

0xDCA avatar Nov 09 '15 19:11 0xDCA

Friendly ping. Just to be clear, I am willing to make the PR, but I want to make sure that it follows your plans.

0xDCA avatar Nov 10 '15 18:11 0xDCA

? Who is the friendly ping directed towards?

webnerdlw avatar Nov 10 '15 18:11 webnerdlw

No one in particular (as no one had answered).

0xDCA avatar Nov 10 '15 18:11 0xDCA

Sorry I have been slammed with work and ui-leaflet. No one else wants to head up this project so I am the bottleneck. Anyway yes this part of the code needs to be refactored.

While your at it I would remove the UUID stuff from the map crap and use scope.$id instead. The other PR has stalled and if this one is cleaner and easier to read; I would rather accept that.

If it is possible the use inheritance or decorators to have different providers instead of "one" does all; then that is preferred as well.

So please PR away.

nmccready avatar Nov 10 '15 20:11 nmccready

was this ever implemented?

Rex90 avatar Apr 16 '16 17:04 Rex90

@armensg I haven't done that yet, sorry.

0xDCA avatar Apr 17 '16 00:04 0xDCA

@0xDCA

FYI I have an Ionic/Cordova app as well and was experiencing similar issues.

I believe I solved this issue within my code by simply setting the preventLoad to true which disables the load handling, and then handling the load code myself with something like this:

module.config(function(uiGmapGoogleMapApiProvider){ uiGmapGoogleMapApiProvider.configure({ key:'XXXXXX' libraries: 'places', preventLoad: true }); });

module.run(function($rootScope,$log,$q,uiGmapGoogleMapApi,$timeout,uiGmapGoogleMapApiManualLoader,$window,uiGmapIsReady,uiGmapPromise){

    // The google.maps object
    var googleMaps;
    var connectionListenerRegistered =  false;



    //Initial Loading of the map
    uiGmapGoogleMapApiManualLoader.load();


//Listen for map failure
    $window.addEventListener('error', function(e) {

        //This error not not relevant to us
        if(!e || !e.target || !e.target.src){
            return;
        }

        srcURL = e.target.src;

        //The google map load failed
        if(srcURL.indexOf('maps.googleapis.com') != -1){
            $log.error("Google map load failed",e.target);

            isReLoading = false;

            //Going register a retry on connect if we have no connection
            registerRetryOnConnect();
        }

    }, true);


    function registerRetryOnConnect(){

        //If we don't have
        if (window.navigator.connection && window.Connection && window.navigator.connection.type === window.Connection.NONE) {
            $log.debug("We don't have a connection so we'll reattempt a load again when we get a connection");

            //We already registered a connection listener
            if(connectionListenerRegistered){
                return;
            }

            document.addEventListener('online', function() {
                attemptMapReLoad();
            });

            connectionListenerRegistered =  true;
        }
        else{
            attemptMapReLoad();
        }
    }

    //Registering an error event listener to determine if the load failed

    /**
     * Attempts a reload of the google map api if it is not already loading
     */
    function attemptMapReLoad(){
        $log.info("Google map not available going to attempt to reload it.");

        if(isReLoading){
            $log.info("Google maps is currently loading. Will not attempt another reload");
            return;
        }

        isReLoading = true;
        uiGmapGoogleMapApiManualLoader.load();
    }
    this.attemptMapReLoad = attemptMapReLoad;


    //Asynchronously loading the google map
    uiGmapGoogleMapApi.then(function(maps) {
        googleMaps =  maps;
    }).finally(function(){
        //Load is complete
        $log.info("Map loading attempt finished");
        isReLoading = false;
    });


    function isMapLoaded(){
        if(googleMaps){
            return true;
        }
    }});

Anyway hope that helps anyone going through the same issue.

vrfurl avatar Sep 29 '16 22:09 vrfurl

@vrfurl Thanks for posting that. Sounds like a reasonable approach to me

0xDCA avatar Sep 30 '16 21:09 0xDCA

You need to install the cordova-plugin-network-information plugin to get the online event fired inside cordova.

edmondchui avatar Oct 31 '18 19:10 edmondchui