REST in Place

                                _______
                               /       \
                               | R.I.P.|
                               |       |
                               |       |
                             -------------

REST in Place is an AJAX Inplace-Editor that talks to RESTful controllers. It requires absolutely no additional server-side code if your controller fulfills the following REST preconditions:

  • It uses the HTTP PUT method to update a record
  • It delivers an object in JSON form for requests with “Accept: application/json” headers

The editor works by PUTting the updated value to the server and GETting the updated record afterwards to display the updated value. That way any authentication methods or otherwise funky workflows in your controllers are used for the inplace-editors requests.

URL: http://github.com/janv/rest_in_place/
REPOSITORY: git://github.com/janv/restinplace.git BLOG: http://jan.varwig.org/projects/rest-in-place

Installation

First, install REST in Place with

script/plugin install git://github.com/janv/rest_in_place.git

To use it, include jquery.rest_in_place.js in your template and execute the following in your document’s onLoad handler:

jQuery(".rest_in_place").rest_in_place();

Other JS Frameworks

Besides the jQuery version, this repository also includes a mootools and a Prototype version (rest_in_place.js and mootols.rest_in_place.js respectively).

REST in Place originally was a mere proof of concept, written in jQuery and Prototype. I haven’t touched it much in a while, but it apparently proved useful to a lot of people and I want to develop it more actively in the future. Unfortunately I never work with neither mootools nor Prototype, so I can only improve the jQuery version of the plugin.

I will happily include contributions for the other versions, but I won’t develop them on my own.

Rails Request forgery Protection

For REST in Place to work with Rails request forgery protection, place the following lines into your applications layout:

<script type="text/javascript">
  rails_authenticity_token = '<%= form_authenticity_token %>'
</script>

Usage Instructions

To make a piece of Text inplace-editable, wrap it into an element (a span usually) with class “restinplace”. The editor needs 3 pieces of information to work: a URL, an object name and the attribute name. These are provided as follows:

  • put attributes into the element, like this:

    <span class="rest_in_place" data-url="/users/1" data-object="user" data-attribute="name">
      <%= @user.name %>
    </span>
    
  • if any of these attributes is missing, DOM parents of the element are searched for them. That means you can write something like:

    <div data-object="user" data-url="/users/1">
      Name:  <span class="rest_in_place" data-attribute="name" ><%= @user.name %></span><br/>
      eMail: <span class="rest_in_place" data-attribute="email"><%= @user.email %></span>
    </div>
    
  • You can completely omit the url to use the current document’s url. With proper RESTful controllers this should always work, the explicit url-attribute is for cases when you want to edit a resource that is displayed as part of a non-RESTful webpage.

  • Rails provides the dom_id helper that constructs a dom id out of an ActiveRecord for you. So, your HTML page may look like this:

    <div id="<%= dom_id @user # == "user_1" %>">
      Name:  <span class="rest_in_place" data-attribute="name" ><%= @user.name %></span><br/>
      eMail: <span class="rest_in_place" data-attribute="email"><%= @user.email %></span>
    </div>
    

    REST in Place recognizes dom_ids of this form and derives the object parameter from them, so that (with the current documents url used) you really only need to provide the attributes name in most cases.

    Note that a manually defined (in the element or in one of the parents)
    object always overrides dom_id recognition.

  • REST in Place supports multiple form types. The default type is a input field, a textarea is also supported. To select a form type use the data-formtype attribute in the element and set it to the name of your formtype ( input, or textarea ).

    To write your own form types, just extend the RestInPlace.forms object and select your new form type throught the data-formtype attribute.

    This feature is only supported in the jQuery version.

Example

Your routes.rb:

map.resources :users

Your app/controllers/users_controller.rb:

class UsersController < ApplicationController
  def show
    @user = User.find params[:id]
    respond_to do |type|
      type.html
      type.json {render :json => @user}
    end
  end

  def update
    @user = User.find params[:id]
    if @user.update_attributes!(params[:user])
      respond_to do |format|
        format.html { redirect_to( @person )  }
        format.json { render :nothing =>  true }
      end
    else
      respond_to do |format|
        format.html { render :action  => :edit } # edit.html.erb
        format.json { render :nothing =>  true }
      end
    end
  end
end

Your app/views/users/show.html.erb:

<html>
<head>
  <%= javascript_include_tag "jquery-1.4.min" , "jquery.rest_in_place" %>
  <script type="text/javascript">
    rails_authenticity_token = '<%= form_authenticity_token %>'
    jQuery(function(){
      jQuery(".rest_in_place").rest_in_place();
    });
  </script>
</head>
<body>
  <div id="<%= dom_id @user %>">
    ID: <%= @user.id %><br />
    Name: <span class="rest_in_place" data-formtype="input" data-attribute="name"><%= @user.name %></span><br/><br/>
    Hobbies: <span class="rest_in_place" data-formtype="textarea" data-attribute="hobbies"><%= @user.hobbies %></span>
  </div>
</body> 
</html>

You can run this example by running to the testapp included in this plugin with script/server (sqlite3 required) and visiting localhost:3000/users/

Hint:
you need to set up the database first. Copy and edit testapp/config/database.yml.sample accordingly. If you don’t want to use the included sqlite3 database, run rake db:create and rake db:schema:load.

Troubleshooting

REST in Place is very picky about correct headers and formatting. If you experience errors, please make sure your controller sends responses as properly formatted JSON with the correct MIME-Type “application/json”.

Non-Rails

REST in Place was written for Ruby on Rails but is usable with any kind of RESTful web api. You should be able to adapt the instructions above to your framework easily.

Participation

I’d love to get comments, bug reports (or better, patches) about REST in Place. For this, you can either fork the project and send a pull request, or submit a bug in the tracker at github: http://github.com/janv/rest_in_place/issues

For general comments and questions, please use the comment function on my blog: http://jan.varwig.org/projects/rest-in-place

Acknowledgements

Thanks to Kevin Valdek for the mootools version (commit 086b409d38932426540f402bb642c66165c78976) and improvements to the testapp (commit 8eb121271345943588fe2a8467c790e7e37f3d7a).

Thanks to nando for commit 17ca4e3060a1420bf13d9b9d89ceeba2bcc144d2

Copyright (c) 2010 [Jan Varwig], released under the MIT license

41 Comments for 'REST in Place'

djot

-
Hi,

how about setting up a demo?

djot

Jan

Thanks for your interest.

Unfortunately I don’t have the resources right now to setup a demo.

You can try it out for yourself very quickly if you just generate a new rails app with a “users” resource, a database table users(id, name) and the controller and view from the readme above. Or, if you already have some RESTful app, just add the span-tag and the include in one of your templates.

Stephen

Jan,

Thanks for the contribution. I encourage you to check out some Rails helpers to simplify things. In the head, for example, you could write:

<%= javascript_include_tag 'jquery-1.2.1', 'rest_in_place' %>

Rails 2.0 helpers “div_for” (and “content_tag_for”) could help simplify the body, too. It would be nice to have a helper in the plug-in that creates the other tags (you could use a block helper, like “form_for”, to capture the model, and then another helper to yield the spans for that model).

Jan

Thanks.

In the most recent revision the javascript and dom_id tags are already used in the README example. I deliberately did not use rails helpers to generate the entire div tag and kept everything simple to not obstruct the main point of this plugin: that no additional server-side code is necessary.

The idea with the block helper is nice though, I might integrate that.

Hi Jan

I’ve implemented as per your instructions in a Rails 2.0 app using prototype. However the field just says “saving…” after clicking enter and the db isn’t updated.

My view code looks like this:
<%=(h @photo.caption) || (h @photo.created_at) %>

(dom_id is identified in a surrounding div)

Controller code is:
def update
@photo = @event.photos.find(params[:id])

respond_to do |format|
  if @photo.update_attributes(params[:photo])
    flash[:notice] = 'Photo was successfully updated.'
    format.html { redirect_to(event_photos_url(@event)) }
    format.xml  { head :ok }
  else
    format.html { render :action => "edit" }
    format.xml  { render :xml => @photo.errors, :status => :unprocessable_entity }
  end
end

end

Any ideas?
thx

Hello Jan
I want to use your plugin but i am getting:
ActionController::InvalidAuthenticityToken.

Somewhere in your rest_in_place.js file should olso be authenticity_token parametr.

Jan

Thanks for the hint!

I’m gonna deal with that problem and release a new Version of the Plugin soon. Right now I’m pretty much occupied with University duties but as soon as time allows it I’m gonna fix this!

Jan,

awesome library.. thanks. Did you get a chance to fix the InvalidAuthenticityToken problem?

Oh and do you also have the plugin on a git(hub) repository by any chance? Might make forking and patching easier…

-J

Jan

I finally updated the Plugin to work with Rails 2.0.
Sorry for taking so long.

I’ll think about the github thing.
SVN doesn’t exactly prevent anyone from forking or patching ;)

nadine

Hi Jan,
i’ve been having big trouble with all other plugins in firefox. this one is ace. thank you a lot!

just one thing:
i installed it on rails 2.0 with prototype and when i press enter “saving…” is still displayed, but it is updated fine in the database.

this is my code:
view:

<div id=”">
title:
description: <span class=”rest_in_place” url=”/categories/}” attribute=”description”>

controller:
def update
@category = Category.find params[:id]
@category.update_attributes!(params[:category])
render :text => CGI.escapeHTML(category.description)
end

it would be great if you (or anyone who ran into the same problem) could help. thanks a lot.

nadine

ah, had a mistake in the last post.
my update-function looks like this:

def update
@category = Category.find params[:id]
@category.update_attributes!(params[:category])
redirect_to @category, :status => :see_other
end

the saving… thing only happens in firefox. safari does it fine!
what am i doing wrong?

Jan

If you have Firebug installed, you should check for the status and body of the AJAX request used to retrieve the updated value.

Since you say it works in Safari I assume that everything is correct in your controllers but whithout more information it’s hard to tell from here what’s going on. I actually developed the Plugin using Firefox.

nadine

thanks for the answer. it’s very weird. everything seems to come back the right way, just the browser doesn’t update. these are the data from firebug:

– snip (really long firebug dump – Jan) –

sorry, for this endless post, i tried to delete the unimportant bits from the response body
it would be great if you could help, i really don’t know what’s wrong, as things in firebug look right to me.

my old value was “old value”
the value i edited it to is “new value”

btw. i integrated the rest_in_place form in my index-file. maybe the action show is not the right one to use for me?

i tested it in IE, Opera, Firefox and Safai. And at the moment it only works in safari for me. it would be great if you could help. thanks.

@Jan : Thanks for the plugin, I needed something like this instead of the in_place_editing plugin.

About the “saving…” problem that Nadine also has problems with :

The problem lays with attributeName. The .to_xml method returns dashes instead of underscores. So you will have this problem if you have an attribute like “user_id”. The XML returned is “user-id”.

So I changed line #17 in rest_in_place.js (Yeah I use Prototype) from

e.innerHTML = Element.select(xhr.responseXML, objectName+' '+attributeName)[0].textContent;

to

e.innerHTML = Element.select(xhr.responseXML, objectName+' '+attributeName.dasherize())[0].textContent;
Jan

Thanks for your feedback.

I’m thinking about changing the plugin from XML to JSON to solve this.

[...] played an important role too). Since then I’ve become accustomed to git and today ported over REST in Place from Subversion to Github. The Github project page ist located at [...]

Zach

Hey Jan,

Great plugin, worked damn near out of the box. Would have too, but I had messed with some stuff. Only problem that I am having is that I can edit it, and it will say “saving…”, and when I refresh the page it is updated. However, when it won’t update the page asynchronously.

Jan

Check you server log and/or check Firebugs Net-module for clues.

@Zach: I had the same problem as you and got it to work by making a very minor change to the jquery js. I’ve forked the plugin and submitted a pull request, but in the meantime you can look at my commit.

[...] updated the README and the project page with new information. The README was also missing the CSRF part in the example which I believe [...]

paul

Do you have any tips on how this could be adapted to work with content that is loaded after the document has loaded. i.e. list items that are loaded dynamically using ajax?

paul

ah, worked it out, i.e. use $(‘#element’).rest_in_place(args) directly in my rjs.

Haven’t looked at the code yet, but I like the idea very much.

The only problem is those non-standard attributes that you’re jamming into the divs and spans are not valid HTML (well, I admit, I only checked them with an XHTML validator; I don’t actually know if they’d be considered valid for any particular other version of HTML or XHTML).

For me that’s a bit of a show-stopper, as I don’t want my pages spitting out validation errors if anyone feeds them into a validator.

Other than that, looks like a great idea though.

Jan

I understand your concern but let’s be honest for a minute ;)

These additional attributes might be undefined in XHTML but are syntactically correct and will just be ignored by browsers. They provide a very convenient and concise way of specifying how to edit the fields.
They pose no actual harm to a user’s browsing experience. Neither directly (in the form of malfunctions) nor indirectly (like what the widespread use of really ill-formed HTML did to web development in the past).

As a computer scientist I understand the desire for absolute correctness.
But as an actual application developer, my users are my target audience, not fellow nerds who like feed my pages into validators (no offense).
A little pragmatism can’t hurt once in a while if you want to get things done.

[...] Please check out the changes at http://github.com/janv/rest_in_place/tree/master and on the project page. [...]

filipe abreu

Awesome plugin, Jan.

Tried other two ‘edit in place’ plugins with no success. Yours is simple and very versatile.

I am just having one difficulty:

Is there anyway of using this plugin with a ‘textfield’ instead of a ‘input’ form element? The thing is that in some places I want a ‘input’ and other places ‘textfield’. Is there an argument in the plugin to do this?

thanks!

filipe abreu

Regarding the last post.

I want to use ‘textarea’, not ‘textfield’.

( I have mistaken Flash with HTML, sorry )

filipe abreu

Alright, a quick hack in the plugin and it is done (with jQuery). I will post the changes in the google code page.

filipe abreu

Oh, i thought the google website was Google Code, not the Rails Google Group. Will deploy to a github fork later.

filipe abreu

Alright, if anyone wants to check the patched version, go to http://github.com/filabreu/rest_in_place/tree/master.

It is also a fork of the original branch made by janv.

It’s really cool.

Sometimes the update will be failed due to validation.

Any ideas about how to integrate this feature to show the error messages?

What i thought is

Create a specific element like div#rest_in_place_error and when ajax update request failed, update the error message into this div.

But this seems be not extensible. What’s your ideas? Thanks.

Very nice plugin. The only problem I am having is with Safari. When I click on a field in a the generated form does not show the oldValue from the table cell in the input field. However, when I inspect the input element it does have the oldValue set in the field. I tried getting around this with some jQuery calls to no avail.

Well, I was able to patch the jquery.rest-in-place.js file which fixed the problems in Safari 3 and a related problem in Safari 4. I think part of my problem might have been that I am using RIP against data in a table.
Here’s the patch ….

4c4
< var oldValue = e.html().replace(/^s|s$/g,”);

var oldValue = e.html();

7d6
< e.find(“input”)[0].focus();
12c11
< });

})

32c31
< });

})

35c34
< };

> }

Ugh … formatting problems there …

I basically made two changes to jquery.rest-in-place.js …
First, I stripped leading/trailing whitespace from the oldValue … this helped alot with my tabular data.

var oldValue = e.html().replace(/^s|s$/g,”);

Then, I added a call after the form is injected to set focus to the input field … this give me good behavior in Safari 3/4.

e.find(“input”)[0].focus();

Rest In Place…

Edit in place is a handy feature where clicking on an area of a web page replaces that portion of the page with a form which will enable the user to update that information.  At one time, this was a part of Rails, but in 2007 it move…

Very interesting and simple plugin Jan. There is however a small bug which took me quite sometime to figure out. The rails_authenticity_token variable which is being passed as authenticity_token needs to be URI encoded. My authenticity token had a ‘+’ character which was getting dropped on passing over HTTP.

Just need to substitute

“&authenticity_token=”+window.rails_authenticity_token
with
“&authenticity_token=”+encodeURIComponent( window.rails_authenticity_token)

Dave

A better regex for stripping leading / trailing that weird white space is: e.html().replace(/(^s)|(s$)/gi,”")

Ben R

Very nice plugin – but had the devil’s own business getting it to play nicely. The problem was after the POST, the JSON GET triggered a 406 response. Adding
format.json { render :json=>@user }
to the show method solved the problem

Jan

Thx Ben, this has been fixed in the latest revision

Alan

Loving the plugin. I had 2 questions hopefully someone can help me with.

  1. After ’saving…’ replaces the text box, it doesn’t disappear after it’s done. I’m sure I’ve messed something up. The data DOES save, just doesn’t refresh the “saving” text. Can someone point me in the general direction on how to troubleshoot this please?

  2. I’d like to send 2 hidden fields simultaneously with a data field when I save a specific attribute. (I probably have to make virtual attributes). Any ideas on how I would do this?

Thanks!

Very nice plugin and easy to get it working. But I wonder, what is the way to display error messages on validation failures? Any suggestions? I see this was asked earlier without a reply.

Write a comment

Trackback: http://jan.varwig.org/projects/rest-in-place/trackback
Comment-Feed: RSS2