/better-edit-in-place

Easy editing in place for RESTful web apps.

Primary LanguageJavaScriptMIT LicenseMIT

Better Edit in Place

The default “in_place_editing” plugin for Rails is disappointing. This is my
solution.

Features

  • Support for nested resources
  • RESTful. Just add a line to your respond_to block to render as JSON.
  • Easy to use outside of Rails. Just add a class name and the path as the
    rel attribute to an element and you’re done.
  • Logic lives in the JavaScript (You may consider this a feature or a flaw. I
    consider it a feature.)
  • Support for text input, text area and select fields
  • Support for foreign key parameters for select fields
  • Customizable. One can set:
    • class for edit element
    • class submit and cancel buttons
    • custom functions to be called on Ajax.Request onLoading and onComplete events
      i.e. to set up a spinner.

The better-edit-in-place script makes it easy to create AJAX edit-in-place
fields in a RESTful web app. Note that if your approach isn’t RESTful,
then this script won’t do much for you.

If you have not installed this plugin using script/plugin install script,
then copy src/editable.js file to your public/javascripts folder.
To load this file with your Rails app simply add the following line to your layout:

<%= javascript_include_tag 'editable' %>

If you’re using the classic way of handling events:

document.observe(‘dom:loaded’, function(event) { Editable.setupAll(); });

Another solution is to use event delegation:

document.delegators(‘click’, { ‘.editable’: function(element) { element.editable(); } });

To make an element editable, give it the class name editable. You can use different
class name, but in this case you need to pass it to Editable.setupAll method
or change CSS selector if you’re using event delegation.

Specify the element’s resource url as the element’s rel attribute, then give it
an id attribute that contains the name of the record’s model, as well as
the attribute to be edited. You can also add the record’s id as well, so
as not to have multiple elements with the same id in the same page. All in,
an editable element’s attributes look like this:

class="editable" id="list_1_name" rel="/lists/1"

When the element is clicked, it will be hidden, and an input will appear
that will allow the user to change the field. You must have an appropriate
respond_to set in your controller that renders the record as a JSON response.

A Rails example:

respond_to do |format| format.html # Whatever format.json { render :json => @list } end

Using it with Rails

If you’re using this plugin in a Rails app, you can use the following view
helper to simplify the markup for your editable element:

edit_in_place(@list, :name)

By default, the edit-in-place tag will be a <span> element. You can override
this using the :tag option:

edit_in_place(@list, :name, :tag => :h1)

Nested Resources

Better Edit in Place also works seamlessly with nested resources. Just pass
in the resources as an array. This will update an item’s name attribute:

edit_in_place([@list, @item], :name)

The list_item_path will be determined automatically. If you wish to
pass a different URL in, you can do that as well:

edit_in_place([list, @item], :name, :url => weird_item_path(item))

If you’re using a newer version of Rails…

You might need to tell ActiveRecord not to include the root while serializing
to JSON. To do so, put this line in an initializer, or environment.rb:

ActiveRecord::Base.include_root_in_json = false

Customizations

Input types

By default input text element is used. To change it to text area or select, you need to set
editField type:

document.delegators(‘click’, { ‘.editable’: function(element) { element.editable({ editField: {’type’: ’textarea’} // or ‘select’ }); } });

Class names

Edit element class name can also be set:

element.editable({ editField: { ‘type’: ‘textarea’, ‘class’: ‘editable-input’ } });

Class name can also be set for submit and cancel buttons:

element.editable({ submitButtonClass: ‘editable-submit’, cancelButtonClass: ‘editable-cancel’ });

Select box options

For select elements you need to specify possible options:

element.editable({ editField: { ‘type’: ‘select’, ‘options’: [[“black”, “1”], [“gray”, “2”], [“white”, “3”]] } });

To load data directly from Rails, you can use to_json method:

element.editable({ editField: { ‘type’: ‘select’, ‘options’: <%= Colors.all.map{|c| [c.name, c.id.to_s]}.to_json %> } });

Foreign keys (only for select boxes)

If edited attribute is a foreign key, you need to explicitly specify it:

element.editable({ editField: { ‘type’: ‘select’, ‘options’: <%= Author.all.map{|c| [c.name, c.id.to_s]}.to_json %>, ‘foreignKey’: true } });

When using Rails helper, you need to refer to the actual object, instead of the foreign key:

edit_in_place(@post, :author)

You also need to set #to_s method on the associated model i.e.

class Author < ActiveRecord::Base def to_s name end end

Ajax.Request callbacks

You can set custom callbacks for Ajax.Request onLoading and onComplete events:

element.editable({ onLoading: function() {alert(“Loading…”)}; onComplete: function() {alert(“Request completed”)}; });

TODO

  • Escape key should cancel (simple event handler)
  • Maybe some more options (but not too many)
  • Instead of adding .json to URL, set proper content-type

Copyright © 2008 Pat Nakajima