Editing Fields in Place

Problem

You want to provide a way to edit some text on a page that avoids the overhead of a traditional web form. An Ajax solution that displays the form elements and saves the edits would be ideal.

Solution

Use Action Controller's in_place_edit_for method with Action View's in_place_editor_field to create a call to a Ajax.InPlaceEditor of the script.aculo.us library.

Set up this example by generating a Book model with:

$ ruby script/generate model Book

and add the following to the generated migration:

db/migrate/001_create_books.rb:

class CreateBooks < ActiveRecord::Migration
 def self.up
 create_table :books do |t|
 t.column :title, :string
 end
 Book.create :title => 'Perl Best Practices'
 Book.create :title => 'Learning Python'
 Book.create :title => 'Unix in a Nutshell'
 end
 def self.down
 drop_table :books
 end end

Next, include the script.aculo.us and Prototype libraries in your layout using javascript_include_tag:

app/views/layouts/books.rhtml:

<html>
 <head>
 <title>Books</title>
 <%= javascript_include_tag :defaults %>
 </head>
 <body>
 <%= yield %>
 </body>
</html>

Call the in_place_edit_for method in the Books controller, passing the object and object attribute as symbols. The controller also defines index and show methods.

app/controllers/books_controller.rb:

class BooksController < ApplicationController
 in_place_edit_for :book, :title
 def index
 @books = Book.find :all, :order => 'title'
 end
 def show
 @book = Book.find(params['id'])
 end end

The default view iterates over the array of books, displaying each as a link to the show action for each.

app/views/books/index.rhtml:

<h1>Books - list</h1>
<ul>
<% for book in @books %>
 <li><%= link_to book.title, :action => 'show', :id => book.id %></li> 
<% end %>
</ul>

Finally, call the in_place_editor_field in the show.rhtml view helper, passing the object and attribute to be edited:

app/views/books/show.rhtml:

<h1>Books - edit</h1>
<b>Title:</b>;
<%= in_place_editor_field :book, :title %>;

Discussion

In-place editing, a feature used in the administration of photo collections on Flickr.com, can really speed up simple edits that shouldn't require a full page refresh. Flickr's use of this effect makes a lot of sense because of the cost of refreshing a page full of photos. Instead, Ajax allows you to update several elements per photorequiring very little bandwidth per edit (see ).

The solution demonstrates the relatively large amount of functionality that you get by including only two methods in your application; in_place_edit_for and in_place_editor_field. The default view (index.rhtml) lists the titles in the books table. Clicking a book link calls the show action, which retrieves a single book object, making it available to the show.rhtml view. The text in the show view initially appears in a span tag. Mousing over it highlights the text; clicking the text replaces the span tag with a form input tag. With the text now appearing in a text field, it can be modified and submitted with the OK button. The user can also cancel the action, which returns the text to a span tag.

There a slight usability issue to be aware of with this style of element editingit's often not obvious when in-place editing is enabled. The solution to this problem is to add instructions or images that make it clear that fields are, in fact, editable.

Figure 8-12. Using Ajax to update a page

See Also