简体   繁体   中英

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.

What you probably want to do here is to prevent the default behavior of your form using in JS :

$('form').submit(function(e) { 
 e.preventDefault();
 var form = $('form');
 var form_data_json = JSON.stringify(form.serializeArray());
 form_plus_geojson = form_data_json.concat(Geojson);

Grab your data in JS ( this should be easy but you dont really say how do you extract the data from mapbox ).

Then send back the data via AJAX ( don't forget validation on either end )

if (my_data_is_not_good/empty/whatever) { 
    console.log('nop')
 } else {
         $.ajax({ 
         type: 'post',
         url: Yourcontrollerpostaction, 
         data: form_plus_geojson,
         dataType: "JSON",

Once the data has been sent you want to resume the normal form submission post behavior and submit the form.

         complete: function() { 
            this.off('submit');
            this.submit();
         }
     });
 }

});

Then you just need to parse the data in your controler.

def post_controller
  if request.xhr?
    my_hash = JSON.parse(params[:mydata]) 
  else 
    normal post behavior 
  end
end

Everything is quick pseudocode you might have to fix some stuff as i haven't tested it but it should work. Obviously now that we are doing everything in ajax you don't need the complet function call back and can remove that part.

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]]}}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM