0
votes

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?

3
Are you trying to avoid duplicated code in the comment creation (before the respond_to block) or in the different js.erb responses?nfm
I want to allow for different js.erb responses depending on which start point I'm coming from. Basically I want to have a re-usable widget that allows for different UI changes depending on context.Ish

3 Answers

3
votes

That's exactly the purpose of Apotomo: http://apotomo.de/

Here is it's own description:

Apotomo is a true MVC widget framework for Rails. Widgets are based on Cells and provide reuseable view components. Having bubbling events, they know when and how to update themselves via AJAX!

Working with Apotomo widgets almost feels like developing GUI components – in a Rails environment.

Have a try, it's great.

2
votes

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.

2
votes

A better solution (than using a hidden_field) might be to check the request.referer in your controller action. This way you leverage the fact that each context has a unique URL, and don't have to explicitly specify another unique value when rendering your widget partial.