Ajax file uploads

Posted by Bryan Larsen.

Note: actually there’s one or two steps missing here: you also need the formSubmit function in hobo-jquery. If you have questions, ping me on the list. Even better, this is built into Hobo 1.4.

With the paperclip plugin installed, Hobo supports file uploads, but does not support Ajax file uploads. It seems like it should work, but the file doesn’t get uploaded. The problem is that browsers do not allow Javascript access to the local file system as a security measure.

To get around this, most sites either use a Flash plugin or use an ugly IFrame hack. We’ll use the JQuery form plugin, which packages up the IFrame hack.

You’ll also need this enhancement to Hobo. You can either install the patch, or use 1.1.0.pre3 or 1.3.0.pre26 (?).

The first step is to install JQuery form plugin, using something like this:

<extend tag="page">
  <old-page merge>
      <javascript name="jquery.form.js"/>

This assumes you’re using the hobo-jquery plugin. See its instructions for more details.

On our application, we have a package that has-many uploads.

class Package < ActiveRecord::Base
  has_many :uploads, :dependent => :destroy

class Upload < ActiveRecord::Base
  fields do
    name :string
  has_attached_file :attachment
  belongs_to :owner, :class_name => "User", :creator => true

Our view is pretty standard Hobo Ajax:

    <div part="attachments-div">
      <table-plus:uploads fields="name, attachment_file_name, attachment_file_size"/>
    <h3>Add an Attachment</h3>
    <form with="&new_for_current_user(this.uploads)" owner="package" message="Uploading..." update="attachments-div" without-cancel>
      <field-list: skip="package"/>

But we need some extra stuff in the controller to correspond to the requirements listed here for the jQuery form plugin.

class UploadsController < ApplicationController
  auto_actions  :all, :except => [:index]
  auto_actions_for :package, [:create, :new]

  def create_for_package
    hobo_create_for :package do
      if valid?
        # we have to ignore the requested file type because the workarounds
        # necessary to send files via ajax don't allow us to set the
        # headers.  So we just assume that we need to send an Ajax
        # response.  We'll also Wrap that response in a textarea so
        # the browser keeps its hands off of it.
        ajax_update_response(params[:page_path], params[:render].values, {}, {:preamble => "<textarea>\nvar _update = Hobo.updateElement;", :postamble => "</textarea>"})
        render(:status => 500,
              :js => "alert(\"#{this.errors.full_messages.join(". ").gsub('\n', '')}\");")

User contributed notes

  • On December 28, 2011 JezChatfield said:

    I found that I had to add more to application.dryml to make this work, without complaint. I added:
  • On December 28, 2011 JezChatfield said:

    <include src="rapid" gem="hobo"/> <include src="taglibs/auto/rapid/cards"/> <include src="taglibs/auto/rapid/pages"/> <include src="taglibs/auto/rapid/forms"/> <include gem="hobo-jquery" /> <include src="paperclip" plugin="paperclip_with_hobo"/>
  • On December 28, 2011 JezChatfield said:

    Without this, I got error messages about "form__for_(Class)" missing error messages.