How to manage file uploads with Backbone.js, paperclip, jquery file upload and...

Feb 19, 2013
A4d868c8478241303110aa2eeddef2af

Hello there,

A few days ago I was looking for a step-by-step tutorial on how to manage file uploads with the tools I mention in the title, and I couldn't find one, so I decided to write one myself!

In the first part of the tutorial, I show you a gallery made in Backbone plus Coffeescript, Haml, Eco templates, the jQuery file upload tool, Rails, Paperclip, and Amazon AWS.

First of all, we need to create a new Rails project:

$ rails new jqueryfileuploadbackbone
$ cd jqueryfileuploadbackbone

The Gemfile configuration should be the following:

 source 'https://rubygems.org'
# Bundle edge Rails instead:     
# gem 'rails', :git =>; 'git://github.com/rails/rails.git'

gem 'haml'
gem 'jquery-rails'
gem 'rails', '3.2.11'
gem 'paperclip', '~>; 3.4.0'
gem 'aws-sdk', '~>; 1.6.0'
gem 'aws-s3'
#gem 'mysql2'
gem 'pg'
gem 'devise'
gem 'jquery-fileupload-rails'
gem 'rb-readline'
gem 'debugger'
# Gems used only for assets and not required
# in production environments by default.
group :assets do
  gem 'backbone-on-rails'
  gem 'backbone-support'
  gem 'coffee-rails', '~>; 3.2.1'
  gem 'eco'
  gem 'haml_assets'
  gem 'sass-rails', '~>; 3.2.3'
  gem 'sass'

   # See https://github.com/sstephenson/execjs#readme for more    supported runtimes
  # gem 'therubyracer', :platforms =>; :ruby

 gem 'uglifier', '>= 1.0.3'
end

group :test, :development do
  gem 'fabrication'
  gem 'guard-rspec'
  gem 'rspec-rails'
end

#  To use ActiveModel has_secure_password
# gem 'bcrypt-ruby', '~>; 3.0.0'

# To use Jbuilder templates for JSON
# gem 'jbuilder'

# Use unicorn as the app server
# gem 'unicorn'

# Deploy with Capistrano
# gem 'capistrano'

# To use debugger
# gem 'debugger'

Now we need to install all the Gems using the next command

  $ bundle install

Now we need to create a model to save our images and additional data, I'll create one called 'qrcodes', where 'image' will be where I save the image name, which is a requirement of the Paperclip Gem.

 $ rails generate model Qrcode description:string
 $ rake db:setup

Now we need to prepare our model to upload images, for this effect just run the next command generator for Paperclip, this is where we specify our model and the attribute where we'll save our images.

   $ rails generate paperclip Qrcode image

Now we need to run all of our migrations

  $ rake db:migrate

Now comes the moment to generate our Backbone structure (models, views, routes, etc), we can use Scaffold like the following:

  $ rails g backbone:install

Now we continue with the creation of our controller and two methods to upload and show all of the images that were transferred.

  $ rails g controller qrcodes index create --skip javascript

After using the previous command, we are going to create our routes to use it, for this we're going to edit the file routes.rb located in

 config/routes.rb

We are going to declare the next two lines if they don't already exist

get 'qrcodes/index'
root to: 'qrcodes#index'

Now we need to delete the public index file, to show the root page index in the qrcodes controller, used in the previous step.

public/index.html.erb

If we have done everything correctly, we can now run our server using the rails server command so we can receive pop alerts in the address:

http://localhost:3000

Now let's modify the following file to enable our routes:

app/assets/javascripts/jqueryfileuploadbackbone.js.coffee

Adding the following code:

  window.Jqueryfileuploadbackbone =
      Models: {}
      Collections: {}
      Views: {}
      Routers: {}
      ManageImages: ->
      new Jqueryfileuploadbackbone.Routers.Qrcodes()
         alert 'in backbone'
         Backbone.history.start()

   $(document).ready ->
      Jqueryfileuploadbackbone.ManageImages()

At this point we need to verify that all works fine. If we had any issues with Javascript we need to check the application.js file. For example, I had to change the original content because I had problems with the Backbone templates and I fixed it like this:

//= require jquery
//= require jquery_ujs
//= require_self
//= require underscore
//= require backbone
//= require jqueryfileuploadbackbone
//= require_tree ../templates/
//= require_tree .//models
//= require_tree .//collections
//= require_tree .//views
//= require_tree .//routers

Now we're going to edit our routers file to show our images list, the file is:

  app/assets/javascripts/routers/qrcodes_router.js.coffee

And add the following content:

 class Jqueryfileuploadbackbone.Routers.Qrcodes extends Backbone.Router
    routes:
        '' : 'showManageImages'
    showManageImages: ->
      @collection = new Jqueryfileuploadbackbone.Collections.Qrcodes()
      @collection.fetch()
      view = new Jqueryfileuploadbackbone.Views.QrcodesIndex collection: @collection
      $('#container-app').html view.render().el

In the previous code we can see that we need to use our collection to show all the images, but it is necesary to declare first the right routes in the config/routes.rb file like the following:

 scope api do
  resources :qrcodes, format: false
end

Now we are going to use our model to fetch all the data from the qrcode table, for that we use the file:

 app/assets/javascripts/models/qrcode.js.coffee

And we are going to specify the routes that we declared in the routes file, this is what I have:

 class Jqueryfileuploadbackbone.Models.Qrcode extends Backbone.Model
   urlRoot: 'api/qrcodes'

In the same way, we are going to edit the collection file

  app/assets/javacripts/collections/qrcodes.js.coffee

Its content is:

 class Jqueryfileuploadbackbone.Collections.Qrcodes extends Backbone.Collection
   url: 'api/qrcodes'
   model: Jqueryfileuploadbackbone.Models.Qrcode

Important note: In this case I'm using Coffescript and it's of great importance to be careful with the indentation spaces at all times.

If you remember, in the Backbone router file we declared an div "container-app", this will be used to attaching our Backbone app in ROR, the view in charge of doing that is:

 app/views/qrcodes/index.html.erb

We are using haml (included in the Gemfile), and now we're going to substitute the file extension .haml for .erb

app/views/qrcodes/index.html.haml

The contents of the file are:

%h1 List of Images
#container-app

Now we are going to edit our qrcodes_index.js.coffee view

 app/assets/javascripts/views/qrcodes_index.js.coffee

As an example, we could use:

class Jqueryfileuploadbackbone.Views.QrcodesIndex extends Backbone.View
  template: JST['qrcodes/index']
  render:->
   @$el.html @template
   @

Continuing working with haml, we need to add the .haml extension to use it in our Eco template. The original file was:

 app/assets/templates/qrcodes/index.jst.eco

Now it should look like this:

 app/assets/templates/qrcodes/index.jst.eco.haml

And we need to a link:

 =link_to 'Add new Image', '#qrcode/add'

And now get to the interesting part, where we deal with file uploads. To do this we will create a new view:

 app/assets/javascripts/views/qrcodes/add_view.js.coffee

The content of the file to use the jQuery File Upload plugin in Backbone is:

 class Jqueryfileuploadbackbone.Views.QrcodesAddView extends Backbone.View
   template: JST['qrcodes/add']
   render: ->
    @$el.html @template
    @uploadQrcode()
   @

  uploadQrcode:=>
    @$el.fileupload
    add: (e, data)->
       $('#qrcode_image').hide()
       $("#fileupload-loading").html 'Cargando...'
       data.submit()


    formData: [
      name: 'authenticity_token'
     value: $("meta[name=\'csrf-token\']").attr('content')
   ]

   done: (e, data) ->
     window.location = '/'

And add template with the following content:

The template file name is:

app/assets/templates/qrcodes/add.jst.eco.haml

The content of the form to upload images is:

= form_tag 'api/qrcodes', :multipart => true, :id => 'fileupload' do
  .fields
    = file_field(:qrcode, :image)
    #fileupload-loading{style: 'height: 50px; width: 200px;'}
    %br

Now let's add our route in qrcodes_router.js.coffee to use our new view to add images

The updated content should be:

class Jqueryfileuploadbackbone.Routers.Qrcodes extends Backbone.Router
   routes:
     '' : 'showManageImages'
     'qrcode/add' : 'addNewImage'
   showManageImages: -> 
      @collection = new Jqueryfileuploadbackbone.Collections.Qrcodes()
      @collection.fetch() view = new Jqueryfileuploadbackbone.Views.QrcodesIndex collection: @collection
      $('#container-app').html view.render().el

   addNewImage : ->
     view = new Jqueryfileuploadbackbone.Views.QrcodesAddView
     $('#container-app').html view.render().el

Now we need to declare our file upload plugin:

   app/assets/javascripts/application.js

And we need to add the jquery file upload Gem (included in the Gemfile)

  //=require jquery-fileupload/basic

This the moment to configure the Paperclip gem to manage uploads in our server, first edit the file

 app/models/qrcode.rb

The last content you need to add is:

class Qrcode < ActiveRecord::Base
 attr_accessible :description, :image
 has_attached_file :image,
    :url => '/system/qrcodes/:filename'

end

Afterwards, we edit the qrcodes_controller.rb file with the following code:

 class QrcodesController < ApplicationController
   respond_to :json

   def index
  end

  def create
    respond_with Qrcode.create(params[:qrcode])
  end
end

Additionally, run all the migrations:

  $ rake db:migrate

And now we can run our server

 $ rails server

Let's test what we did. First, click in the add new image link. Then, browse for an image with the right format in the form. If the file uploads, we have a working application!

Locally, the file should be located in the public/qrcodes directory.

In another tutorial I will teach you how to list and delete images.

Hope you found this tutorial useful. You can find the repo in github at:

 https://github.com/heridev/jqueryfileuploadbackbone

See you next time!

blog comments powered byDisqus