11.21.07

Setting data on hidden fields during form submit using AJAX (Google maps geocode example)

Posted in Programming, Ruby, rails at 7:30 pm by Robert Horvick

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:

  1. Ultimately adding a marker requires a geocode.
  2. 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]).
  3. 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:

  1. Accept the data in a standard address form.
  2. Contact the google web service for geocode creation
  3. Cache the geocode with the location record in SQL
  4. 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:

  1. Get the data from the user
  2. Perform the geocode lookup in javascript on form submit
  3. Note the geocodes on a pair of hidden form fields
  4. 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?

[Slashdot] [Digg] [Reddit] [del.icio.us] [Facebook] [Technorati] [Google] [StumbleUpon]

4 Comments »

  1. Ferdinand Svehla said,

    November 21, 2007 at 9:17 pm

    Very smart idea to push the geocoding to the client.

  2. 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?

  3. Dave Abad said,

    June 25, 2008 at 4:09 pm

    Rob,

    Great idea. I’ve added this to my toolkit.

    Thanks!

    Dave

  4. Lyloalilk said,

    October 6, 2008 at 8:42 pm

    Подскажите шооблончег под Wordpress 2.6.2, чтобы был похож на ваш http://www.ghostonthird.com.

    Заранее благодарю)

Leave a Comment