I am having a problem with the Google maps API, after upgrading it to version 3.35. The goal is to display the map in a form so that the user can define the geolocation. I am using Django 1.11.12 and CanJS 2.0.2.
Here is the code (reduced for simplicity) which works fine with the Google maps API version 3.34, but not with the version 3.35.
models.py
from django.db import models
from maps.models import JSONField
class Institution(models.Model):
geolocation = JSONField(blank=True, null=True)
maps/models.py
from django.db import models
from maps.forms import LatLngWidget
class JSONField(models.TextField):
def formfield(self, **kwargs):
kwargs.update({'widget': LatLngWidget()})
return super(JSONField, self).formfield(**kwargs)
maps/forms.py
from django import forms
from django.conf import settings
from django.contrib.staticfiles.templatetags.staticfiles import static
from django.utils.safestring import mark_safe
from django.template.loader import render_to_string
from django.forms.widgets import Widget
class LatLngWidget(Widget):
def __init__(self, attrs=None):
super(LatLngWidget, self).__init__(attrs)
self.width = 640
self.height = 480
def render(self, name, value, attrs=None):
data = dict(name=name, value=value, width=self.width, height=self.height)
html = render_to_string("maps/widget.html", data)
return mark_safe(html)
def _media(self):
return forms.Media(css={"all": [static("maps/css/gmap.css"),],},)
media = property(_media)
widget.html
<div class="map-container" id="geolocation-container">
<div class="map-box">
<div class="map map-canvas" id="gmap-geolocation"></div>
</div>
</div>
<script src="https://maps.googleapis.com/maps/api/js?v=3.35&key=xxx"></script>
<script type="text/javascript">
Map = can.Control({
init: function(el, options) {
this.map = null;
this.marker = null;
this.name = options.name;
this.lat = options.lat || 0;
this.lng = options.lng || 0;
this.zoom = options.zoom || 2;
this.createMap();
},
createMap: function() {
var data = {
'name': this.options.name,
'width': this.options.width,
'height': this.options.height,
'lat': this.lat,
'lng': this.lng,
'zoom': this.zoom
};
var mapOptions = {
zoom: 5,
center: {lat: -25.344, lng: 131.036},
};
var target = 'gmap-'+this.name;
console.log(target);
this.map = new google.maps.Map(document.getElementById(target), mapOptions);
// This code works with 3.34. Initial map is rendered with 3.35, but without buttons and not updated if zooming.
// 3.34
// > gmap-geolocation
// 3.35
// > gmap-geolocation
// > gmap-undefined
// > Uncaught TypeError: Cannot read property 'firstChild' of null js?v=3.35&key=xxx:70
// > gmap-undefined
//this.map = new google.maps.Map(document.getElementById('gmap-geolocation'), mapOptions);
// This code works with 3.34. This code does not work at all with 3.35 - browser freezes.
},
});
{% autoescape off %}
var data = {% firstof value "{}" %};
{% endautoescape %}
data.name = "{{ name }}";
data.width = "{{ width }}";
data.height = "{{ height }}";
new Map($("#geolocation-container"), data);
</script>
edit.html
<form method="post" action="" enctype="multipart/form-data">
{% csrf_token %}
{% formrow form.geolocation %}
</form>
The problem appears to be in widget.html. Once again, if the entire code stays the same, and only the Google maps API is upgraded from 3.34 to 3.35, I get the error "Uncaught TypeError: Cannot read property 'firstChild' of null".
I would appreciate any tip how to fix this.
The issue has been fixed by renaming the CanJS control function "Map". If in widget.html
Map = can.Control({
is changed to
Map2 = can.Control({
... and the instance created with
new Map2($("#geolocation-container"), data);
... then everything works fine again with the Google maps javascript API v3.35.