Search code examples
ruby-on-railsruby-on-rails-3partialsujs

What's the best way to do UJS in rails when you have a re-usable widget?


In my current project I have a couple instances where I have a re-usable form that exists inside a rails partial. This form submits to a specific controller via ajax (:remote => true). The controller does some stuff and then returns back the appropriate js.erb to modify the page via javascript.

This works fine for when I have a single view. But the problem seems to happen when this re-usable partial exists on multiple views. In view 1 I might want to issue a completely different set of javascript commands then in view 2.

As a concrete example, say I have a comments controller that has the normal CRUD operations.

I now have partial called _comments_box.erb. This _comments_box.erb contains the ability to submit a comment via a simple line:

- form_for comment, :url => post_comments_path(post), :remote => true do |f|

This submits to a comments_controller.rb create method which looks somethings like this:

def create
   ... do some stuff, like create a new comments model

   respond_to do |format|
      # will respond with create.js.erb
      format.js
   end

end

The create.js.erb in turn adds a comment to the view, perhaps doing a bunch of other updates to the DOM.

Say I render the _comments_box.erb within a view called post_summary.erb. Now I have another view, post_detail.erb that requires the same _comments_box.erb. However the post_detail.erb requires me to update completely different divs on the DOM in response to a new comment.

I need to create a different JS response for each instantiation. So I can either:

  • Create an alternate controller method, say create_2. Pass in some parameter to the _comments_box.erb from post_detail.erb to the _comments_box.erb partial so it knows which controller method to fire. This will allow me to have a separate file _create_2.js.erb that will allow me to manipulate the post_detail.erb view independently.
  • Forget about using js.erb altogether and just use plain old AJAX and get back JSON, and handle the javascript manipulation completely on the client-side.

It seems option 1 allows me to continue to use the UJS supported by Rails which is nice. But also means I probably will be adding a lot of duplicate code everywhere which is annoying. Is there a way for me to do this elegantly while continuing to use UJS?


Solution

  • I'd not recommend using UJS for frontend apps: server shouldn't take care of client side business. I agree it's useful and clean but it lacks performance and thus should be kept for backend stuff (RJS will move into a gem, see here: http://weblog.rubyonrails.org/2011/4/21/jquery-new-default).

    That said, back to the solutions you expose:

    • 1) I think you won't need an extra controller, you'd just have to pass additional params in order to know from where to query came from. A hidden_field could do the trick. With this info, render the good js.erb file

      format.js { if condition
                   render "create.js.erb"
                  else
                    render "create_2.js.erb"
                  end
                }
      
    • 2) I'd go for it and return json but you'll face the same problem: knowing from where the request comes from.