(first posted: Feb 11, 2008)
(3140 Reads)
keywords: in_place_editor REST
Permalink
Restful In Place Editor
I was bothered that using the standard in_place_editor helpers for Rails required I add new actions to my controllers and corresponding routes in routes.rb.
As of Rails 2.0 it's is now a plug-in rather than part of core. I should probably make my changes a separate plug-in, or better, submit this as a patch. Instead, for now, I suggest you install the plug-in and copy and hack the code.
To install,
$ script/plugin install http://svn.rubyonrails.org/rails/plugins/in_place_editing/
Open the file vendor/plugins/in_place_editing/lib/in_place_macros_helper.rb and copy the two methods, in_place_editor and in_place_editor_field to your app/helpers/application_helper.rb
in_place_editor helper
We add a new option to this helper:
:as Name of the param the value is returned into
e.g. :as => 'foo[name]'
To the in_place_editor method, add the following line after the js_options['callback'] = line:
js_options['callback'] = "function(form) { return #{options[:with]} }" if options[:with]
# this line is the only change
js_options['callback'] = "function(form,value) { return '#{options[:as]}=' + escape(value) }" if options[:as]
in_place_editor_field helper
In this we make just 2 changes. First, the default :url will now be your standard :update action in the controller. And 2nd, the new value will be set using standard REST parameters, eg params[:foo => { attribute_name => value }]
# Renders the value of the specified object and method with in-place editing capabilities.
def in_place_editor_field(object, method, tag_options = {}, in_place_editor_options = {}) tag = ::ActionView::Helpers::InstanceTag.new(object, method, self)
tag_options = {:tag => "span", :id => "#{object}_#{method}_#{tag.object.id}_in_place_editor", :class => "in_place_editor_field"}.merge!(tag_options)
in_place_editor_options[:url] ||= { :action => "update", :id => tag.object.id, :method => :post, :_method => :put }
in_place_editor_options[:as] = "#{object}[#{method}]"
tag.to_content_tag(tag_options.delete(:tag), tag_options) + in_place_editor(tag_options[:id], in_place_editor_options)
end
Your controller
Last but not least you do things differently in your controller than the standard in_place_editor.
You do not need to use "in_place_edit_for" at all! Forgetaboutit!!
Rather, you just change the #update action to respond to ajax calls, as follows:
# PUT /foo/1
def update
@foo = Foo.find(params[:id])
success = @foo.update_attributes(params[:foo])
respond_to do |format|
format.html do
if success
flash[:notice] = 'Foo was successfully updated.'
redirect_to(@foo)
else
render :action => "edit"
end
format.js do
# assume updating only one attribute
attribute = params[:foo].keys.first.to_s
render :text => self.class.attributes.include? attribute ? @foo[attribute] : '(bad attribute)'
end
end
end
Basically we update the attributes in params, as usual. Except when its an ajax call, we assume there's really only one attribute in the params being updated. And the ajax renders the new value for updating the page.
A bit unconventionally, I do the update_attributes first then respond based on format. That's because, as mentioned in other blogs, there's no easy way to handle validations when doing in_place_editing. But then again it can be done (google it), and change your #update action as needed.
Views
There's nothing to change in the views, they work the same as the standard in_place_editor, for example
<%= in_place_editor_field "foo", :title %>
PS Thanks for the leethal comments... :)
UPDATE March 4, 2008
I found some views reference attributes better handled from a different controller than the current one, like associations of the current resource. I've modified the in_place_editor_field to use a controller based on the resource object name rather than assume the current controller.
I also added support for a :formatter option, so you can run the content through a filter before updating the view. For example, I use BlueCloth formatting, and specify :formatter => 'markdown' on my text attributes.
Here's the full helper:
def in_place_editor_rest(object, method, tag_options = {}, in_place_editor_options = {})
tag = ::ActionView::Helpers::InstanceTag.new(object, method, self)
tag_options = {:tag => "span", :id => "#{object}_#{method}_#{tag.object.id}_in_place_editor", :class => "in_place_editor_field"}.merge!(tag_options)
in_place_editor_options[:url] ||= { :controller => object.pluralize, :action => "update", :id => tag.object.id, :method => :post, :_method => :put }
in_place_editor_options[:as] = "#{object}[#{method}]"
# changed to support inline formatter
if formatter = in_place_editor_options.delete(:formatter)
in_place_editor_options[:load_text_url] ||= { :controller => object.pluralize, :action => 'show', :id => tag.object.id, :attribute => method.to_s }
var = @template.instance_variable_get("@#{object}")
value = var.send(method)
content = content_tag(tag_options.delete(:tag), @template.send( formatter, value), tag_options)
else
content = tag.to_content_tag(tag_options.delete(:tag), tag_options)
end
content + in_place_editor(tag_options[:id], in_place_editor_options)
end
<%= will_paginate @contexts %>




Restful In Place Editor
Posted by: Ian on February 23, 2008 05:17 AM#