8

Suppose I have a Single Page Application (Angular JS application) , and I draw a Google Map instance on an element id googleMap -

var mapInstance = new google.maps.Map(document.getElementById(`googleMap`), mapOption)

then I navigate through the application routing , due this , destroying the googleMap DOM element , and finally I back to the route with this element , now I have to re-draw the map on this element .

What is the correct way to redraw the map ?

As I read in this answer I don't have to re-create it , but use the same instance .

Community
  • 1
  • 1
URL87
  • 10,667
  • 35
  • 107
  • 174

4 Answers4

5

I would be careful with

$scope.on('$destroy', function(){
    mapInstance = null;
})

I had a directive containing my map DOM element, and was calling this method to remove all map references from the datalayer, all listeners, and then setting the map to null. I was checking the heap between page navigation and the map instance was being created anew, but the old map(s) were still in the heap leading to ever increasing memory usage.

Also the answer you linked recommends re-using your map instance instead of trying to remove it. The google maps developers also recommend this approach. The solution I found was to pass your directive element to a service, and append a child element to that creating the map on the new child element. If the map already exists, just append the map div to the directive element. Here is my code below.

ng-view Element

<map-container></map-container>

Directive

angular.module('project')
  .directive('mapContainer', function($timeout, mapService) {
     return {
       template: '<div></div>',
       restrict: 'E',
       replace: true,
       link: function(scope, element) {
         $timeout(function () {
           //Use the $timeout to ensure the DOM has finished rendering
           mapService.createMap(element).then(function() {
              //map now exists, do whatever you want with it
           });
        });
      }
    };
 })

Service

angular.module('project')
  .service('mapService', function($q) {

    var lat = -33.1798;
    var lng = 146.2625;
    var minZoom = 5;
    var maxZoom = 20;
    var zoom =  6;
    var mapOptions = null;
    var map = null;

    function initialiseGmap(element) {

      return $q(function (resolve) {
        if (map) {
          //Map already exists, append it
          element.append(map.getDiv());
          resolve();
        } else {
          //No map yet, create one
          element.append('<div id="map_canvas"></div>');

          mapOptions = {
            zoom: zoom,
            center: new google.maps.LatLng(lat, lng),
            styles: hybridMap, //your style here
            minZoom: minZoom,
            maxZoom: maxZoom,
            mapTypeId: google.maps.MapTypeId.ROADMAP,
            mapTypeControl: false,
            streetViewControl: false,
            panControl: false,
            scaleControl: true,
            zoomControl: false
          };

          map = new google.maps.Map(document.getElementById('map_canvas'), mapOptions);

          //create any map listeners you want here. If you want to add data to the map and add listeners to those, I suggest a seperate service.

          resolve();
        }
      });
    }

    return {
      createMap: function(elem) {
        return initialiseGmap(elem);
      },
      getMap: function() {
        return map;
      },
      //Create as many functions as you like to interact with the map object
      //depending on our project we have had ones to interact with street view, trigger resize events etc etc.
      getZoom: function() {
        return zoom;
      },
      setZoom: function(value) {
        map.setZoom(zoom);
      }
    };
  });
Jags
  • 1,639
  • 1
  • 16
  • 30
2

This question has the angularjs tag so I assume this is an Angular JS application. In which case, you should probably shouldn't be doing this in your page controller.

You could use a pre-existing directive like https://angular-ui.github.io/angular-google-maps/#!/ or you could write your own directive.

If you write your own directive then you should destroy the google map instance each time the directive is destroyed using the $scope.on('$destroy', fn) event. Like this..

$scope.on('$destroy', function(){
    mapInstance = null;
})
jonhobbs
  • 26,684
  • 35
  • 115
  • 170
  • yeap , it's Angular JS application – URL87 Mar 29 '16 at 11:14
  • In a case I write my own directive , what I have to do in the `$destroy` function and how to redraw it ? – URL87 Mar 29 '16 at 11:17
  • I've updated my answer with how to destroy it. I'm not sure why you need to re-draw it as the DOM it's attached to is gone. Just create a new mapInstance when the new directive is compiled. – jonhobbs Mar 29 '16 at 11:25
  • I did see the stuff in your link to the other question about not destroying and recreating the map. I'm not sure this is common practice and you're not going to be able to do it if you create the mapInstance in your directive. You'd need to create a single mapInstance at a higher level in your app and reuse it. You might want to dig around in the angular-google-maps code and see how they do it. – jonhobbs Mar 29 '16 at 11:29
  • I have added an answer to this question that I feel addresses the issue. I would appreciate a look over. It is working for me and I am able to reuse and access the same instance across the single page application. As someone with limited Stackoverflow experience, if either/both of you feel this answer addresses the question, what is the best method for making sure others are directed to the answer? @URL87 – Jags Feb 15 '17 at 02:05
2

Every time you call:

map = new google.maps.Map(document.getElementById('xx'), mapOptions);

You CONSUME your Google maps quota. Be careful!

0

In my case helped re-initialization of Google Map with timeout solved the issue.

 setTimeout(function(){ initialize() }, 30);

Killing initialized instance possible by destroying previously dynamically created element, but this is harder...

K.Alex
  • 91
  • 3