Question

I have a project with an "Article" scaffold - which includes a paperclip file field for the thumbnail - and others on the team are complaining about how they have to add the file to the field again when they submit the form and a validation error is triggered due to missing data on another field.

Figuring that was due to browser limitations, I added a remote => true to the form, along with an error.js.erb file, figuring the file field would persist if the page didn't have to be reloaded. Unfortunately that wasn't the case, as I read that browsers are unable to process multipart forms / files through AJAX for security reasons. However, I then discovered the Remotipart gem which solves this issue. So the relevant parts of my application looks like the following...

_form.html.erb

<%= form_for(@article, :html => { :multipart => true }, :remote => TRUE) do |f| %>
  <div id="errors"></div>
  <%= f.text_field :title %>
  <%= f.text_area :body %>
  <%= f.file_field(:photo) %>
  <%= f.submit %>
<% end %>

articles_controller.rb (Create action)

  def create
    @article = Article.new(params[:article])

    respond_to do |format|
      if @article.save
        format.html { redirect_to(@article, :notice => 'Article was successfully created.') }
        format.xml  { render :xml => @article, :status => :created, :location => @article }
      else
        format.html { render :action => "new" }
        format.xml  { render :xml => @article.errors, :status => :unprocessable_entity }
        format.js { render 'errors', :locals => { :item => @article } }
      end
    end
  end

errors.js.erb

<%= remotipart_response do %>
    $("#errors").empty().append('<div id="error_explanation"><h2><%= pluralize(item.errors.count, "error") %> prohibited this article from being saved:</h2><ul></ul></div>');
    <% item.errors.full_messages.each do |msg| %>
        $("#error_explanation ul").append("<li><%= escape_javascript(msg) %></li>");
    <% end %>
<% end %>

So basically, if there are validation errors, the js file adds those errors to the errors div on the form. In addition, the file field persists if filled in. It all works: it DOES create the content in the database and upload the file, OR throw up the errors without losing the file field if something fails validation, but one problem remains.

When the form is submitted with a file uploaded, I get a 406 not permitted error in my log without a redirect to the show page. If the form does NOT have a file uploaded, then the log returns a 200 OK, but the page also is not redirected to the show action.

After hunting on Google and other SO threads, I found this bit of code that supposedly would pass it the correct headers (and yes, jQuery is installed and runs before application.js)...

application.js

jQuery.ajaxSetup({ 
  'beforeSend': function(xhr) {xhr.setRequestHeader("Accept", "text/javascript")}
})

...unfortunately it doesn't work. I'm out of ideas, any suggestions on how to beat that 406 issue and make this properly redirect?

Was it helpful?

Solution 2

Haven't been able to come back to this issue until now, but I finally solved this. First off, I dropped the application.js code. Second, the 406 redirect was solved by including a format.js in the respond_to's save success...

respond_to do |format|
  if @article.save
    format.js
    format.html { [...] }
  else
    [...]
  end
end

Only issue is, the page would not redirect at all, even if a redirect_to was placed in the format.js. It did trigger a create.js.erb file, so inside of that, I put...

window.location.replace("/articles");

...to initiate the redirect upon loading create.js.erb. I would rather it respond and redirect via the controller rather than a javascript redirect, but I have yet to see a working solution to this issue (anything else I researched involving request.xhr code didn't work at all). But this works.

However, a side benefit to doing it this way is that I can get more creative with the article saving, such as previewing the 'show' page for a few seconds before redirecting to the articles index, etc.

OTHER TIPS

I think that you are making this too complex, why not focus on preventing the users from uploading the file again after an error in the first place. The users continue on their merry way and you don't have to mess with ugly JavaScript hacks.

Store the file and redirect the user to an the create page with the file field filled in.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top