Reputation: 632
I'm using Turbolinks & Google Maps 4 Rails. For performance reasons, I'm trying to have all the javascript load async.
I find that the map page loads correctly if I'm coming from another page from within the same website. But if I just type in the address in the browser and go to the maps page, I get the two errors:
Uncaught ReferenceError: Gmaps is not defined
Uncaught ReferenceError: jQuery is not defined
Here is my setup:
In my head (in haml)
%script{ src:'//maps.google.com/maps/api/js?v=3.13&sensor=true&libraries=geometry', type:'text/javascript' }
-# http://www.rickypai.com/blog/2013/02/22/web-dev-gotchas-with-rails-4-0-turbolinks/
- if Settings.ASYNC_JAVASCRIPT
= render 'layouts/async_javascript', path: 'application'
- else
= javascript_include_tag 'application', data_turbolinks_track: true
_async_javascript.html.erb
<%# http://railscasts.com/episodes/369-client-side-performance?view=asciicast %>
<script type="text/javascript">
(function() {
var script = document.createElement('script');
script.type = 'text/javascript';
script.async = true;
script.src = '<%= j javascript_path(path) %>';
var other = document.getElementsByTagName('script')[0];
other.parentNode.insertBefore(script, other);
}) ();
</script>
application.js
//= require modernizr
//= require jquery
//= require jquery.turbolinks
//= require google.maps.infobox
//= require google.maps.richmarker
//= require google.maps.markerclusterer
//= require gmaps/google
...
//= require turbolinks
Then finally, in the body (in haml)
- present @location, LocationPresenter do |p|
= render 'map_coffee', id: 'l_map', markers: [p.marker].to_json, lat: p.latitude, lng: p.longitude
_map.coffee.html.haml
:coffee
class RichMarkerBuilder extends Gmaps.Google.Builders.Marker
create_marker: ->
options = _.extend @marker_options(), @rich_marker_options()
@serviceObject = new RichMarker options
rich_marker_options: ->
marker = document.createElement 'div'
marker.setAttribute 'class', 'marker_container'
marker.innerHTML = @args.marker
_.extend @marker_options(), { content: marker }
create_infowindow: ->
return null unless _.isString @args.infowindow
boxText = document.createElement 'div'
boxText.setAttribute 'class', 'infowindow_container'
console.log @args
boxText.innerHTML = @args.infowindow
@infowindow = new InfoBox(@infobox(boxText))
@bind_infowindow()
infobox: (boxText)->
content: boxText
pixelOffset: new google.maps.Size(0, 0)
boxStyle:
width: '280px'
handler = Gmaps.build 'Google', { builders: { Marker: RichMarkerBuilder }, markers: { maxRandomDistance: null } }
handler.buildMap
provider: {}
internal:
id: '#{id}'
, ->
json = #{markers}
if json
markers = handler.addMarkers json
handler.bounds.extendWith markers
handler.fitMapToBounds()
if json.length == 1
handler.getMap().setZoom #{Settings.LOCATIONS_DEFAULT_ZOOM}
else
handler.map.centerOn { lat: #{lat}, lng: #{lng} }
handler.getMap().setZoom #{Settings.LOCATIONS_DEFAULT_ZOOM}
Does anyone know how to set this up correctly? I find that there is much documentation with regards to this, which is surprising since Turbolinks is standard for Rails 4. Any help would greatly be appreciated!
For those who are also struggling with this, I'll update the Gmaps4Rails wiki as soon as I understand how to do this!
Upvotes: 1
Views: 711
Reputation: 632
Best way (and cleanest) I figured out is to add the javascript to the asset pipeline. This will ensure that JQuery is available. So something like this in some coffeescript file:
$('#map').mappy()
Where you define the JQuery plugin Mappy like this:
(($, window) ->
class Mappy
# defaults:
constructor: (el, options) ->
@$el = $(el)
# @options = $.extend({}, @defaults, options)
@id = @$el.attr 'id'
@lat = @$el.data 'lat'
@lng = @$el.data 'lng'
@markers = @$el.data 'markers'
@handler = Gmaps.build 'Google', { builders: { Marker: RichMarkerBuilder }, markers: { maxRandomDistance: null } }
@handler.buildMap
provider: {}
internal:
id: @id
, =>
json = @markers
if json
markers = @handler.addMarkers json
@handler.bounds.extendWith markers
@handler.fitMapToBounds()
if json.length == 1
@handler.getMap().setZoom $.LOCATIONS_DEFAULT_ZOOM
else
@handler.map.centerOn { lat: @lat, lng: @lng }
@handler.getMap().setZoom $.LOCATIONS_DEFAULT_ZOOM
class RichMarkerBuilder extends Gmaps.Google.Builders.Marker
create_marker: ->
options = _.extend @marker_options(), @rich_marker_options()
@serviceObject = new RichMarker options
rich_marker_options: ->
marker = document.createElement 'div'
marker.setAttribute 'class', 'marker_container'
marker.innerHTML = @args.marker
_.extend @marker_options(), { content: marker }
create_infowindow: ->
return null unless _.isString @args.infowindow
boxText = document.createElement 'div'
boxText.setAttribute 'class', 'infowindow_container'
console.log @args
boxText.innerHTML = @args.infowindow
@infowindow = new InfoBox(@infobox(boxText))
@bind_infowindow()
infobox: (boxText)->
content: boxText
pixelOffset: new google.maps.Size(0, 0)
boxStyle:
width: '280px'
$.fn.extend mappy: (option, args...) ->
@each ->
$this = $(this)
data = $this.data('mappy')
if !data
$this.data 'mappy', (data = new Mappy(this, option))
if typeof option == 'string'
data[option].apply(data, args)
) window.jQuery, window
Then make the data available by doing something like this in your haml
- present @location, LocationPresenter do |p|
#map{ data: { markers: [p.marker].to_json, lat: p.latitude, lng: p.longitude } }
Upvotes: 2