# Terminal
rails g scaffold places name latitude:decimal longitude:decimal
rails webpacker:install:stimulus
# layouts/application.html.erb
<%= javascript_include_tag "https://maps.googleapis.com/maps/api/js?key=#{ENV['MAPS_API_KEY']}&libraries=places&callback=dispatchMapsEvent",
async: true,
defer: true,
"data-turbolinks-eval": false %>
# packs/application.js
window.dispatchMapsEvent = function (...args) {
const event = document.createEvent("Events")
event.initEvent("google-maps-callback", true, true)
event.args = args
window.dispatchEvent(event)
}
# javascript/controllers/maps_controller.js
import { Controller} from "stimulus"
export default class extends Controller {
static targets = ["field", "map", "latitude", "longitude"]
connect() {
if (typeof (google) != "undefined"){
this.initializeMap()
}
}
initializeMap() {
this.map()
this.marker()
this.autocomplete()
console.log('init')
}
map() {
if(this._map == undefined) {
this._map = new google.maps.Map(this.mapTarget, {
center: new google.maps.LatLng(
this.latitudeTarget.value,
this.longitudeTarget.value
),
zoom: 17
})
}
return this._map
}
marker() {
if (this._marker == undefined) {
this._marker = new google.maps.Marker({
map: this.map(),
anchorPoint: new google.maps.Point(0,0)
})
let mapLocation = {
lat: parseFloat(this.latitudeTarget.value),
lng: parseFloat(this.longitudeTarget.value)
}
this._marker.setPosition(mapLocation)
this._marker.setVisible(true)
}
return this._marker
}
autocomplete() {
if (this._autocomplete == undefined) {
this._autocomplete = new google.maps.places.Autocomplete(this.fieldTarget)
this._autocomplete.bindTo('bounds', this.map())
this._autocomplete.setFields(['address_components', 'geometry', 'icon', 'name'])
this._autocomplete.addListener('place_changed', this.locationChanged.bind(this))
}
return this._autocomplete
}
locationChanged() {
let place = this.autocomplete().getPlace()
if (!place.geometry) {
// User entered the name of a Place that was not suggested and
// pressed the Enter key, or the Place Details request failed.
window.alert("No details available for input: '" + place.name + "'");
return;
}
this.map().fitBounds(place.geometry.viewport)
this.map().setCenter(place.geometry.location)
this.marker().setPosition(place.geometry.location)
this.marker().setVisible(true)
this.latitudeTarget.value = place.geometry.location.lat()
this.longitudeTarget.value = place.geometry.location.lng()
}
preventSubmit(e) {
if (e.key == "Enter") { e.preventDefault() }
}
}
# views/places/_form.html.erb
<%= form_with model: place, local: true, data: {
controller: :maps,
action: "google-maps-callback@window->maps#initializeMap"
} do |form| %>
<div class="field">
<%= form.label :name %>
<%= form.text_field :name, class: 'form-control' %>
</div>
<div class="field">
<%= form.label :search %>
<%= form.search_field :search, name: nil, class: 'form-control', data: { target: "maps.field", action: "keydown->maps#preventSubmit" } %>
</div>
<div class="field">
<%= form.label :latitude %>
<%= form.text_field :latitude, class: 'form-control', data: { target: "maps.latitude" } %>
</div>
<div class="field">
<%= form.label :longitude %>
<%= form.text_field :longitude, class: 'form-control', data: { target: "maps.longitude" } %>
</div>
<%= content_tag :div, nil, data: { target: "maps.map" }, class: 'map' %>
<div class="actions">
<%= form.submit class: 'btn btn-primary' %>
</div>
<% end %>
# stylesheets/places.scss
.map {
width: 100%;
min-height: 400px;
}
# views/places/show.html.erb
<%= image_tag "https://maps.googleapis.com/maps/api/staticmap?zoom=15&size=400x300¢er=#{@place.latitude},#{@place.longitude}&key=#{ENV['MAPS_API_KEY']}", alt: "Map" %>