Using respond_to ... format.json and jQuery Form Plugin by malsup
Asked Answered
T

5

5

I'm having a tad bit of trouble getting the jQuery Form Plugin to work properly with a file-upload field. When I use the plugin to submit the form without a file-upload field, the format.json portion of the respond_to do |format| block is called properly. However, by adding the file-upload field, it only executes the format.html portion which makes my javascript code think that an error has occurred.

Has anyone run into this before or know a way to force the plugin to always use json? Alternatively, can I modify the url that the plugin uses to force Rails to render the json?

Thanks very much for any help! Code below:

# app/controllers/details_controller.rb
def create
  @detail = Detail.new(params[:detail])

  style = params[:detail_style].to_sym || :thumb
  data = { :id => '5', :url => 'test.rails' }

  respond_to do |format|
    if @detail.save
      flash[:notice] = 'Your image has been saved.'
      data = { :id => @detail.id, :url => @detail.data.url(style) }

      format.html { redirect_to :action => 'index' }
      format.json { render :json => "<textarea>#{data.to_json}</textarea>", :status => :created }
    else
      format.html { render :action => 'new' }
      format.json { render :json => @detail.errors, :status => :unprocessable_entity }
    end
  end
end

/* app/views/sidebar/_details.html.erb (excerpt) */

<% form_for(Detail.new, :html => { :multipart => true } ) do |f| %>
  <%= hidden_field_tag 'detail_style', 'thumb' %>

  <%= f.label :image, "Recent Images" %>
  <%= f.file_field :image%>

  <p> 
    <%= f.submit "Upload" %>
  </p>
<% end %>

<script>
$(document).ready(function() {
  var options = {
    dataType: 'json',

    success: function(json, statusText) {
      console.log("success: " + json);
    },

    error: function(xhr, statusText, errorThrown) {
      console.log("error: " + xhr.responseText);
    }
  };

  $('#new_detail').ajaxForm(options);
});
Thicken answered 9/2, 2010 at 14:53 Comment(0)
E
1

Apparently, setting Accept header is not possible when ajaxSubmit is used and a file is uploaded.

See malsup's answer in this thread here.

He says:

File uploads don't use ajax, the form plugin only makes it appear that way. A true browser submit takes place when uploading a file.

Expecting answered 30/5, 2010 at 9:12 Comment(1)
I tried setting the Accept header, in the beforeSend callback, but this had no effect. When i looked at the jQuery.form's code, the XHR object that was passed to beforeSend has a empty function initialized to "setRequestHeader". However i am looking at 2.16 version of the file, perhaps there are changes in the later versions.Expecting
L
7

Several things are needed to make jQuery Form plugin work for file uploads with a JSON response. In the javascript, the options for .ajaxForm should have:

dataType: 'json', // evaluate return as JSON

The browser needs to tell the Rails action to return content as JSON, one way to do this is add a hidden format input field in the file upload form template:

<%= hidden_field_tag 'format', 'json' %>

The Rails action will then run the format.json method inside the respond_to block.

In the server action

  • encapsulate JSON in a tag, .ajaxForm will unwrap the string and eval JSON correctly
  • set return content type to “text/html”, otherwise some browsers (Firefox) will try to download the return into a file.

e.g.

  respond_to do |format|
    format.json {
          render :json => "<textarea>#{data.to_json}</textarea>", :content_type => "text/html"
    }
  }
Louth answered 17/9, 2010 at 17:59 Comment(0)
P
2

I did a workaround for this problem. I tested this solution only with Rails 3, but maybe it works also for the 2.3.x

The solution is very simple:

!#javascript

$('form#my_form').live('submit',function(){ 
 $(this).ajaxForm({ dataType: "script", success: processJson})
 return false;
})

//data arrive as a string but we can parse it correctly

function processJson(data, statusText, xhr, $form){
 my_data_parsed = JSON.parse(data); 

  }

!#ruby 
def create
....   
render :js =>  { :status => false,:messages => @myobject.errors.full_messages}.to_json 

end 
Pseudohermaphroditism answered 26/1, 2011 at 18:52 Comment(0)
W
1

Can you examine the post to the server with firebug, and paste the request headers here. We're looking to see that the Accept header is set to json. Also, what version of rails are you using?

Wolfe answered 9/2, 2010 at 15:58 Comment(1)
Unfortunately, that's part of the problem. It seems that the plugin is using an iframe to submit the form, thus, it's not showing up in Firebug (I'm not honestly sure this is the problem, but it doesn't show up, and I know it's using an iframe so...). When it works properly, I verified that the header is set to json. Oh and I'm using Rails 2.3.5.Thicken
T
1

I'm still not sure why it doesn't work, but I tried many things and finally found the solution.

I tried changing the url by adding .json to the end which forced Rails to render the format.json block, but it confused my browser by making it think I wanted to download a file.

So, I then tried modifying the url by passing the parameter ?format=json which unfortunately did the exact same thing.

Eventually, I tried changing the format.json to just format.js (Javascript) and adding .js to the url, but still using the same render :json => ... that I had before along with setting the dataType parameter in my jQuery call to json. This appears to work even though it's not the most optimal solution.

I hope somebody else finds this useful. I'll post again if I find a proper answer. In the meantime, if anybody else has a proper answer, let me know and I'll accept yours!

Thicken answered 11/2, 2010 at 2:54 Comment(1)
Works perfectly fine in Firefox, but it does yield a warning in Safari (5.0.5).Cardinale
E
1

Apparently, setting Accept header is not possible when ajaxSubmit is used and a file is uploaded.

See malsup's answer in this thread here.

He says:

File uploads don't use ajax, the form plugin only makes it appear that way. A true browser submit takes place when uploading a file.

Expecting answered 30/5, 2010 at 9:12 Comment(1)
I tried setting the Accept header, in the beforeSend callback, but this had no effect. When i looked at the jQuery.form's code, the XHR object that was passed to beforeSend has a empty function initialized to "setRequestHeader". However i am looking at 2.16 version of the file, perhaps there are changes in the later versions.Expecting

© 2022 - 2024 — McMap. All rights reserved.