Tag Archives: ajax

RESTful in-place edit in Rails and jRails/jQuery

I’ve been using jRails in my recent Rails projects, the original Rails in-place editing plugin uses script.aculo.us, there is a jRails version of it, but neither of them is RESTful – they both create extra actions to update the in-place edit fields.

I found janv’s rest_in_place plugin and it uses the default update action to update the field, so no routes modifications are necessary. I had some problems with the plugin at first but after a pull-request correspondence the plugin now works well. Here are the highlights on how to use it, keep in mind that I use HAML.

The plugin’s init.rb doesn’t load anything for you, so you have to go into your application layout and include the js file:

    = javascript_include_tag 'jquery.rest_in_place.js'

If you have CSRF protection on, this plugin also requires you to set a javascript var. If you have jRails it automatically append the token in ajax requests, but then you would have to modify the plugin a bit to get it to work.

:javascript
  rails_authenticity_token = '#{form_authenticity_token}'

In your controller’s show action, handle the javascript response:

  def show
    respond_to do |format|
      format.html # show.html.erb
      format.js   { render :json => @model }
    end
  end

then you can render the helper in the views:

- div_for @model do
  %p
    = label_tag "Name"
    %br
    %span.rest_in_place{ :attribute =>'name' }
      =h @model.name
  %p
    = label_tag "Location"
    %br
    %span.rest_in_place{ :attribute => 'location' }
      =h @model.location

autocomplete with multiple related form fields (in rails)

I?m an advisor and consultant for a social network site where you go upload images and tag the stuff you own and the stuff you want. One of the thing we wanted to do in the stuff-tagging form was to have related autocompletion fields. In this case, we want an autocompletion of brand names, and then based on the brand name input, we narrow down the autocompletion for models to only the models for that brand. This should be a pretty commonly done, but the auto_complete plugin in Rails has no easy way to do this.

The obvious approach was to use Rails? autocompletion plugin to do the first field, and then use Scriptaculous directly for the second field, my first approach looked like this:

<%= text_field_with_auto_complete :product, :brand %> 

<input id="product_model" name="product[name]" size="30" type="text" />
<div class="auto_complete" id="product_model_auto_complete"></div>

<script type="text/javascript">
  new Ajax.Autocompleter(
    'product_model',
    'product_model_auto_complete',
    '/products/auto_complete_for_product_model',
    { parameter: 'brand=' + $F('product_brand') }
  );
</script>

But this didn?t work because the parameters are populated on instantiation of the Ajax.Autocompleter, so dynamic form content are not properly sent to the controller. Took some googling and playing around to figure this out, but the solution is to append the additional parameter by overriding the callback.

<%= text_field_with_auto_complete :product, :brand %> 

<input id="product_model" name="product[name]" size="30" type="text" />
<div class="auto_complete" id="product_model_auto_complete"></div>

<script type="text/javascript">
  new Ajax.Autocompleter(
    'product_model',
    'product_model_auto_complete',
    '/products/auto_complete_for_product_model',
    { callback: function(e, qs) {
        return qs + '&brand=' + $F('product_brand');
      }
    }
  );
</script>

Just for fun, I?m including the ajax endpoint to show how I generate the results in the desired unordered list format using the auto_complete_result helper. You should always specify a limit in these type of queries.

def auto_complete_for_product_model
  @results = Product.find_by_sql(
  [ %Q{select p.name from brands b, products p
      where lower(b.name) = ? 
      and p.brand_id=b.id 
      and lower(p.name) like ? limit 10}, 
    "#{params[:brand].downcase}", 
    "#{params[:product][:name].downcase}%" ]) if params[:brand] && params[:product][:name]    
    render :inline => "<%= auto_complete_result @results, 'name' %>"
end

BTW, this site is gonna make an appearance at the Techcrunch 50 demo pit, I should be there, check my Tweets, stop by and say hi if you happen to be around.