Search code examples
javascriptruby-on-railsajaxpostgeojson

Post GeoJSON data with form data to Rails 4 app


I've got a web page that creates an "Activity." Using simple form, the user fills in some basic information about the activity:

#new.html.erb
<%= simple_form_for @activity do |f| %>
    <%= f.input :name, label: "Activity name", input_html: { id: "create_activity_name_field" }, hint: "At least 7 characters" %>
    <%= f.input :type, label: "Activity type", input_html: { id: "create_activity_type_field" }, as: :select, :include_blank => false, collection: Activity.types.keys.to_a %>
    <%= f.input :date_field, input_html: { id: "create_activity_date_field" }, as: :string %>
    <%= f.input :time_field, input_html: { id: "create_activity_time_field" }, as: :string %>
    <% f.button :submit, :class => "btn btn-primary" -%>
<% end %>

However, I am also using MapBox to display a map. The user will click the points on the map to make a route for the activity. When the user is done drawing the route, I can then extract GeoJSON data for this route.

I would like to use javascript to POST the GeoJSON data with the form data. On the backend, I'll have rails convert the GeoJSON to a KML file, and use Paperclip to upload it to Amazon S3. The rest of the data can be saved, and Paperclip will give me a URL to the KML file which I can associate with the activity.

I'm a Rails noob, and I can't figure out how to do this nor track down anything to get me over this hurdle. I considered using javascript's FormData. I was very attracted to this approach, because implementation looks so simple, but apparently it can only really handle key/value pairs, not complex nested JSON data.

Any suggestions or strategies greatly appreciated. And greatly appreciated++ if anyone can give detailed answers because, like I said, I'm a few weeks fresh to rails and web development.


Solution

  • Kudos to @jDay for pointing me in the right direction -- specifically with concatenating the JSON data. However, I couldn't get his specific solution to work on my app, and I went trying another method which, in my view, was a little more straightforward.

    I used the Rails simple_form_for gem to make the form, specifying a remote post call:

    #new.html.erb
    <%= simple_form_for @activity, :url => '/activities', :method => :post, :remote => true, html: {id: :activity_create_form, "data-type" => :json} do |f| %>
        <%= f.input :name, label: "Activity name", input_html: { id: "create_activity_name_field" }, hint: "At least 7 characters" %>
        <%= f.input :type, label: "Activity type", input_html: { id: "create_activity_type_field" }, as: :select, :include_blank => false, collection: Activity.types.keys.to_a %>
        <%= f.input :date_field, input_html: { id: "create_activity_date_field" }, as: :string %>
        <%= f.input :time_field, input_html: { id: "create_activity_time_field" }, as: :string %>
        <%= f.button :submit, :class => "btn btn-primary", input_html: {id: "create_activity_submit_btn"} -%>
    <% end %>
    

    Then I used jQuery to to hijack the form submit button and append an invisible form element bearing the GeoJSON data. The data, however, had to be stringified so it could be sent:

    #activity_new.js
    $("form").submit( function (e) {
            e.preventDefault();
    
            $('<input />')
                        .attr('type', 'hidden')
                        .attr('name', 'routeGeoJson')
                        .attr('value', JSON.stringify(polyline.toGeoJSON()))
                        .appendTo(this);
            return true;
    });
    

    (Here, "polyline" is a Leaflet L.polyline object that I used to draw on the map.)

    In the controller, you'll get params like this:

    {"utf8"=>"✓", "activity"=>{"name"=>"A jaunt in the woods", "type"=>"walk", "date_field"=>"2015-09-08", "time_field"=>"07:00"}, "routeGeoJson"=>"{\"type\":\"Feature\",\"properties\":{},\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-118.38855654001236,33.95361274499209],[-118.36624056100845,33.97681937760982]]}}", "commit"=>"Create Activity", "controller"=>"activities", "action"=>"create"}

    Since the "routeGeoJson" data is still in string form, I reassembled the GeoJSON data by parsing it using the JSON gem (which I believe is included by default in Rails 4):

    def create
       geojson = JSON.parse(params[:routeGeoJson])
       # do something...
    end
    

    And you get your GeoJSON hash:

    {"type"=>"Feature", "properties"=>{}, "geometry"=>{"type"=>"LineString", "coordinates"=>[[-118.42014223337173, 33.98407904797006], [-118.37825685739517, 33.956175751601826]]}}