11.21.07
Setting data on hidden fields during form submit using AJAX (Google maps geocode example)
Using heroku I’m working on a small rails app that needs to integrate with google maps.
The Plan
I want to do is allow a user to provide a location which I will store and later be able to render on a google map.
Now, to use Google Maps to store location data you want to be using geocodes (i.e. longitude/latitude values). There are a few reasons for this:
- Ultimately adding a marker requires a geocode.
- Google limits how many address-to-geocode conversions a site can perform (I’ve seen daily and weekly limits - they were 15,000 and 50,000 respectively [which doesn't add up - but whatever]).
- Converting from address to geocode takes a second or longer. Why make the viewer wait when the information only needs to be gathered once?
So the plan was to do the following:
- Accept the data in a standard address form.
- Contact the google web service for geocode creation
- Cache the geocode with the location record in SQL
- Subsequent requests would use the cached record
Reality Bites
The problem is that heroku does not allow outgoing connections so I can’t perform #2. So I need to get the information from the caller without actually making them enter it. What I came up with was:
- Get the data from the user
- Perform the geocode lookup in javascript on form submit
- Note the geocodes on a pair of hidden form fields
- Submit using javascript
Get the data from the user
Let’s get right to it. This is the form code in the rails view (index.rhtml):
<%= form_tag( {:controller => “location”, :action => “create”},
{’id’ => ‘frmAddress’, ‘method’ => ‘post’, ‘onSubmit’ => ’showAddress(this); return false;’} ) %>
<p>
<%= text_field_tag( :address ) %>
<%= hidden_field_tag( :lat ) %>
<%= hidden_field_tag( :lng ) %>
<%= submit_tag(”Go”) %>
</p>
<div id=”map_canvas” style=”width: 500px; height: 300px”></div>
<%= end_form_tag %>
Time for a little breakdown:
<%= form_tag( {:controller => “location”, :action => “create”},
{’id’ => ‘frmAddress’, ‘method’ => ‘post’, ‘onSubmit’ => ’showAddress(this); return false;’} ) %>
We are submitting the form to the location controller’s “create” action. We will submit using the post method. The ‘id’ isn’t needed in this example - ignore it. When the submit button is clicked we will call the showAddress javascript function and pass the form as the parameter. So what’s with return false?
OMG - this is so important. I can NOT stress this enough.
When submitting a form via javascript you must (MUST) have this return false. If you don’t the form will submit twice - once via the javascript submit and once via the normal form post.
Say it with me - if javascript is submitting the form I will return false. I will return false!!!
By the way - Agile Web Development With Rails had this information on 537 (form_remote_tag and remote_form_for documentation).
<%= hidden_field_tag( :lat ) %>
<%= hidden_field_tag( :lng ) %>
These are the hidden fields for the longitude and latitude.
Everything else is plain-jane form stuff.
Lookup, record, submit
function showAddress(frm) {
if (geocoder) {
geocoder.getLatLng(
frm.address.value,
function(point) {
if (!point) {
alert(address + ” not found”);
} else {
frm.lat.value = point.lat();
frm.lng.value = point.lng();
frm.submit();
}
}
);
}
This is mostly the stock google sample code except that I am storing away the latitude and longitude in the hidden form fields and I changed the function to take the form instead of the address value.
Also I submitted the form.
Now in the controller’s create method I can access the parameters like:
def create
@lat = params[:lat]
@lng = params[:lng]
@address = params[:address]
end
And display them in the view like:
Long: <%= @lng %><br/>
Lat: <%= @lat %><br/>
Address: <%= @address %><br/>
So it’s not the server-side solution I had hoped for but it gave me a chance to try something AJAX-ish and to make use of a more complex form submission approach.
Alright … now what have I done wrong?
Ferdinand Svehla said,
November 21, 2007 at 9:17 pm
Very smart idea to push the geocoding to the client.
Girish said,
April 22, 2008 at 12:35 am
Okay, I’m desperate for an answer to this. For some reason, my form won’t submit with the following code:
geocoder.getLatLng(
frm.address.value,
function(point) {
if (!point) {
alert(address + ” not found”);
} else {
frm.lat.value = point.lat();
frm.lng.value = point.lng();
frm.submit();
}
}
);
If I move the frm.submit out of the getLatLng() call, it will submit, but then the lat and lng values will not get updated before the submit. It almost seems like a timing issue because of the asynchronous nature of the getLatLng() call.
If I can submit within the function(point) {} I would be happy, but I can’t get it to submit even though I have the exact same code as you.
Any ideas?
Dave Abad said,
June 25, 2008 at 4:09 pm
Rob,
Great idea. I’ve added this to my toolkit.
Thanks!
Dave
Lyloalilk said,
October 6, 2008 at 8:42 pm
Подскажите шооблончег под Wordpress 2.6.2, чтобы был похож на ваш http://www.ghostonthird.com.
Заранее благодарю)