27

I am considering migrating from backbonejs to angularjs.

In backbone I am able to initialize a view at which point I create an instance of google map. I can then pan/zoom/etc and switch between views and not lose the current state of the map.

Given the following using angularjs:

layout.html

<body>
  <div class="container-fluid" ng-view></div>

map.html

<div id="map_canvas" ng-controller="MapCtrl"></div>

I was able to create a directive to render a map just fine. Problem is that it reloads the map each time I switch back to the map view.

<map></map>

So from what I am learning about Angular, I figured I would create a MapController and initialize the map there. No success.

Bottom line is I need to async-init a google map and push data to it locally or remotely AND be able to navigate the app without RELOADING the map from scratch each time.

Can someone suggest the correct approach?

Thank you :)

Attempt Per Andy Joslin suggestion:

In app.js:

// Generated by CoffeeScript 1.3.3
(function() {
  "use strict";

  angular.module("ofm", ["ofm.filters", "GoogleMaps", "ofm.directives"]).config([
    "$routeProvider", "$locationProvider", function($routeProvider, $locationProvider) {
      $routeProvider.when("/", {
        templateUrl: "partials/home"
      }).when("/map", {
        templateUrl: "partials/map",
        controller: MapCtrl
      }).otherwise({
        redirectTo: "/"
      });
      return $locationProvider.html5Mode(true);
    }
  ]);

}).call(this);

In services.js:

angular.module('GoogleMaps', []).
  factory('wasMapInitialized', function() {
    console.log("inside service");
    var maps = 0;

    if (!maps) {
      maps += 1;
      return 0;
    } else {
      return 1;
    }
  });

In controllers.js:

function MapCtrl($scope) {
  if (!GoogleMaps.wasMapInitialized()) {
    var lat = 46.87916;
    var lng = -3.32910;
    var map_id = '#map';
    initialize(map_id, lat, lng);
  }
  function initialize(map_id, lat, lng) {
    var myOptions = {
      zoom : 8,
      center : new google.maps.LatLng(lat, lng),
      mapTypeId : google.maps.MapTypeId.ROADMAP
    };
    var map = new google.maps.Map($(map_id)[0], myOptions);
  }
}

In map.html

#map
<div ng-controller="MapCtrl"></div>

I get Error: Unknown provider: GoogleMapsProvider <- GoogleMaps

jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
Larry Eitel
  • 1,397
  • 5
  • 19
  • 37

4 Answers4

19

Since views create and destroy controllers when the view changes, you want your maps data to persist somewhere else. Try creating a GoogleMap service which will store the data.

myApp.factory('GoogleMaps', function() {
  var maps = {};

  function addMap(mapId) {
    maps[mapId] = {};
  }
  function getMap(mapId) {
    if (!maps[mapId]) addMap(mapId);
    return maps[mapId];
  }

  return {
    addMap: addMap,
    getMap: getMap
  }
});

function MyController($scope, GoogleMaps) {
  //Each time the view is switched to this, retrieve supermanMap
  $scope.map = GoogleMaps.getMap('supermanMap');

  $scope.editMap = function() {
    $scope.map.kryptonite = true;
  };
}

If you don't like this solution, there are some other ways to do it too.

Andrew Joslin
  • 43,033
  • 21
  • 100
  • 75
  • Could you shortly outline the other options (just naming them)? Thank you. – Torsten Engelbrecht Jun 26 '12 at 02:03
  • 2
    You could create the map in $rootScope, which is never destroyed, and have child scopes inherit it. You could also not use ng-view so the scopes and dom elements aren't created destroyed, but instead are shown and hidden, but you'd have to roll your own routing system. You could also create a maps thing which is just a plain old JavaScript object outside angular that your angular controllers reference (but this is really just a poor way of doing a service) – Andrew Joslin Jun 26 '12 at 12:16
  • Andy, Very nice of you to suggest some ideas. I am so green at Angular. Any chance you can stub out an example? I am stuck in the mud and spinning my wheels. Thank you :) – Larry Eitel Jun 26 '12 at 15:56
  • 1
    You're initializing your service wrong. Here's an example JSFiddle: http://jsfiddle.net/mL34U/ – Andrew Joslin Jun 26 '12 at 20:40
  • 1
    Andy, Thank you very much. Your suggestion worked great. I will post actual code used ASAP. – Larry Eitel Jun 26 '12 at 22:25
  • Andy, what you've described, should it be able to reload the whole Google map from the 'maps' object in memory the second time you view the Angular 'view'? ie. without having to call `new google.maps.Map($('map_canvas')[0], myOptions);` again? – Matty J Dec 05 '13 at 03:13
1

This is what I am doing in Backbone to keep the view instead of destroying it. Suppose you have a div with id="container" and your router have the corresponding routes.

routes: {
    "":"home",
    "map": "showMap"
},

showMap: function() {
    $("#container").children().detach();
    if(!this.mapView) {
        this.mapView = new MapView(); 
        this.mapView.render();
    }
    $("#container").html(this.mapView.el);
}
Jerry
  • 131
  • 3
  • 9
0

Here's my solution for using the maps api with Angularjs routeProvider: in your index you have to add:

angular-google-maps.min.js and lodash.min.js

in application.js:

(function() {

var app = angular.module("myApp", ["ngRoute", "uiGmapgoogle-maps"]);


app.config(function(uiGmapGoogleMapApiProvider) {
    uiGmapGoogleMapApiProvider.configure({
        key: 'YOUR KEY HERE',
        v: '3.17',
        libraries: 'weather,geometry,visualization'
    });
});

app.config(function($routeProvider) {
    $routeProvider
        .when("/home", {
            templateUrl: "home.html",
            controller: "HomeController"
        })
        .when("/workwith", {
            templateUrl: "workwith.html",
            controller: "WorkwithController"
        })




    .otherwise({
        redirectTo: "/home"
    });

});

})();

Last But not least in your controller:

(function() {
var app = angular.module("myApp");
var MyController = function($scope, $http, $log, $location, $routeParams, uiGmapGoogleMapApi) {
    $log.info("MyController");
    $log.info($routeParams);

    // Define variables for our Map object
    var areaLat = 44.2126995,
        areaLng = -100.2471641,
        areaZoom = 3;

    uiGmapGoogleMapApi.then(function(maps) {
        $scope.map = {
            center: {
                latitude: areaLat,
                longitude: areaLng
            },
            zoom: areaZoom
        };
        $scope.options = {
            scrollwheel: false
        };
    });    
 };
app.controller("MyController", MyController);

})();

TacoEater
  • 2,115
  • 20
  • 22
0

Hi Larry Eitel and everyone, I have the following approach:

First, we need to create some global variables:

var mapGlobal, flag=0, CurrentmapNode;

Then in Controller:

    function MapCtrl($scope) {
    var lat = 46.87916;
    var lng = -3.32910;
    var map_id = '#map';

     if (flag==0)
       initMap(mapId, lat, lng);
     else
       reinitMap(mapId);

  function initMap(map_id, lat, lng) {
    var myOptions = {
      zoom : 8,
      center : new google.maps.LatLng(lat, lng),
      mapTypeId : google.maps.MapTypeId.ROADMAP
    };
    mapGlobal = new google.maps.Map($(map_id)[0], myOptions);

    if (typeof mapGlobal != 'undefined')
       flag=1;
  }

  function reinitMap(mapId){
     $(mapId)[0].append(CurrentmapNode);
   }

   $scope.$on("$destroy", function(){
        CurrentmapNode = mapGlobal.getDiv(); //save current map in an auxiliar variable
   });

}

The "on destroy" function saves current map state in another global variable, then, when view is recreated, we don't need to create another map instance.

Creating another map instance can cause memory leak and increase Google maps API usage each time view is recreated.

Best regards

Unheilig
  • 16,196
  • 193
  • 68
  • 98
Edison Urquijo
  • 159
  • 1
  • 3