Recipe 8.1. Adding DOM Elements to a Page


Problem

You need to add elements to a form on the fly, without going through a complete request/redisplay cycle; for example, you have a web-based image gallery that has a form for users to upload images. You want to allow trusted users to upload any number of images at a time. In other words, if the form starts out with one file upload tag, and the users want to upload an additional image, they should be able to add another file upload element with a single click.

Solution

Use the link_to_remote JavaScript helper. This helper lets you use the XMLHttpRequest object to update only the portion of the page that you need.

Include the Prototype JavaScript libraries in your controller's layout.

app/views/layouts/upload.rhtml:

<html>   <head>       <title>File Upload</title>     <%= javascript_include_tag 'prototype' %>   </head>    <body>       <%= yield %>   </body>  </html>

Now place a call to link_to_remote in your view. The call should include the id of the page element that you want to update, the controller action that should be triggered, and the position of new elements being inserted.

app/views/upload/index.rhtml:

<h1>File Upload</h1> <% if flash[:notice] %>   <p style="color: green;"><%= flash[:notice] %></p> <% end %> <% form_tag({ :action => "add" },                       :id => id, :enctype =>                      "multipart/form-data") do %>     <b>Files:</b>     <%= link_to_remote "Add field",                        :update => "files",                        :url => { :action => "add_field" },                        :position => "after" %>;     <div >       <%= render :partial => 'file_input' %>     </div>       <%= submit_tag(value = "Add Files", options = {}) %> <% end %>

Create a partial template with the file input field:

app/views/upload/_file_input.rhtml

<input name="assets[]" type="file"><br />

Finally, define the add_field action in your controller to return the HTML for additional file input fields. All that's needed is a fragment of HTML:

app/controllers/upload_controller.rb:

class UploadController < ApplicationController   def index   end   def add     begin          total = params[:assets].length       params[:assets].each do |file|         Asset.save_file(file)       end            flash[:notice] = "#{total} files uploaded successfully"      rescue         raise        end     redirect_to :action => "index"    end    def add_field     render :partial => 'file_input'   end end

app/models/asset.rb:

class Asset < ActiveRecord::Base   def self.save_file(upload)     begin          FileUtils.mkdir(upload_path) unless File.directory?(upload_path)       bytes = upload       if upload.kind_of?(StringIO)         upload.rewind         bytes = upload.read        end            name = upload.full_original_filename       File.open(upload_path(name), "wb") { |f| f.write(bytes) }       File.chmod(0644, upload_path(name) )      rescue         raise        end   end   def self.upload_path(file=nil)     "#{RAILS_ROOT}/public/files/#{file.nil? ? '' : file}"    end  end

Discussion

The solution uses the link_to_remote function to add additional file selection fields to the form.

When the user clicks the "Add field" link, the browser doesn't perform a full page refresh. Instead, the XMLHttpRequest object makes its own request to the server and listens for a response to that request. When that response is received, JavaScript on the web page updates the portion of the DOM that was specified by the :update option of the link_to_remote method. This update causes the browser to refresh the parts of the page that were changedbut only those parts, not the entire web page.

The :update option is passed "files," matching the ID of the div tag that we want to update. The :url option takes the same parameters as url_for. We pass it a hash specifying that the add_field action is to handle the XMLHttpRequest object. Finally, the :position option specifies that the new elements of output are to be placed after any existing elements that are within the element specified by the :update option. The available options to :position are: :before, :top, :bottom, or :after.

Figure 8-1 shows a form that allows users to upload an arbitrary number of files by adding file selection elements as needed.

Figure 8-1. A form that uses JavaScript to add more input elements to itself.


See Also

  • Section 8.10"




Rails Cookbook
Rails Cookbook (Cookbooks (OReilly))
ISBN: 0596527314
EAN: 2147483647
Year: 2007
Pages: 250
Authors: Rob Orsini

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net