Rendering view for another controller?

I asked this last week but nobody answered. Surely in the age of AJAX
someone knows how to do this…

Let’s say I have a Foo object, and the Foo object has Comment objects
associated with it:

  • I have a Foo controller, with a view displaying information about the
    Foo (including comments).
  • I have a CommentsAjaxController that contains all of the remote
    methods for dealing with Comments

If the Foo page has a form that allows a comment to be added and invokes
CommentsAjaxController#add_comment, how can I get that remote method to
render views/foo/add_comment.rjs? Or–in the general case–how can I
get the view conventions to work the way they always do, but to have it
look for the correct view belonging to an originating controller rather
than the invoked controller?

Is this concept something that is just very wrong, so wrong that nobody
has ever done it or knows how?

If this is the incorrect approach, how do people typically keep generic
remote functions separated away from each controller/view that uses
them?

def some_controller

Maybe there’s some logic here, then

render( :template=> ‘another_controller/view_template_name’ )
end

I’ve tried that, and it didn’t work. Maybe I’ll try it again, but it
acted like it couldn’t find the .rjs template from the other controller.
Assuming it was some dumb mistake on my part, and I try again and rails
finds the file, would the rendered template have access to all of the
member variables for the remote controller? When I used render :file it
found the file, but, of course, partials didn’t work because it looked
in the folder for the remote function’s controller.

Also, you may be interested in the section on “Rendering inline
JavaScriptGenerator page updates” in the ActionController::Base#render
documetnation

I make heavy use (in this particular app) of inline RJS. The problem is
that I would like remote functions to be reusable and to contain ZERO
knowledge of the view. I would like each view to have total control of
how to render the results of the action. The problem, of course, is
that the action has the member variables with all of the data, and there
doesn’t appear to be a way to have the remote function’s controller
render a view from the originating controller as its own (unless the
render :template will make the remote controller data available to the
template).

Does that make any sense?

I’ll try render :template again whenever I get the chance, so maybe this
is all moot.

I’m having trouble understanding the question, so I suspect maybe others
are and that’s why you haven’t gotten an answer.

What code are you using to call your remote method? remote_function ?

remote_function normally points to a controller, right? You want it to
point to a controller that’s in an other-than-default view? Just tell it
that:

remote_function(:update => ‘div_name’, :url => { :controller =>
:some_other, :action => :my_action, :id => maybe_an_id })

But I don’t think that’s your problem. I think you’re finding your
controller okay, but then you want this action to return a view from
another controller? Okay, that’s easy too:

def some_controller

Maybe there’s some logic here, then

render( :template=> ‘another_controller/view_template_name’ )
end

Okay,but I don’t think that’s what you want either? Maybe if you respond
to this explaining how I got it wrong, someone will be able to figure
out what you need.

Also, you may be interested in the section on “Rendering inline
JavaScriptGenerator page updates” in the ActionController::Base#render
documetnation
(ActionController::Base)
for an alternative to using rjs templates—you can put your rjs-type
code directly in the controller. In which case, if you want an action in
one controller to just render the exact same thing as an action in
another controller would, I think this might work:

def some_controller
render(:controller=>“other_controller”, :action=>“some_action”)
end

Hope that gives you some ideas. I’m trying to build up some karma so
maybe I’ll get lucky and someone will answer my unanswered questions. :slight_smile:

Jonathan

Dave S. wrote:

I asked this last week but nobody answered. Surely in the age of AJAX
someone knows how to do this…

Let’s say I have a Foo object, and the Foo object has Comment objects
associated with it:

  • I have a Foo controller, with a view displaying information about the
    Foo (including comments).
  • I have a CommentsAjaxController that contains all of the remote
    methods for dealing with Comments

If the Foo page has a form that allows a comment to be added and invokes
CommentsAjaxController#add_comment, how can I get that remote method to
render views/foo/add_comment.rjs? Or–in the general case–how can I
get the view conventions to work the way they always do, but to have it
look for the correct view belonging to an originating controller rather
than the invoked controller?

Since comments usually only exist as associated with other resources,
you probably don’t want to have a controller to deal with comments
(unless you want users to be able to interact with comments directly).
Controllers are for handling user actions.

If a view needs to be shared between controllers, make it a partial and
place it in a common folder (/views/shared/ is the common rails
convention, possibly with a subfolder). If the view needs setup, place
the setup method in a module (usually in /lib, I like to name these
modules like CommentsAssistant), include it in any controller that needs
to do the setup, and call it from the appropriate action. Protect the
setup method so it can’t be called as an action itself. If the setup
needs to be shared between EVERY controller, place it in
ApplicationController (or include the module into it).

Dave S. wrote:

Is this concept something that is just very wrong, so wrong that nobody
has ever done it or knows how?

It is kinda wrong actually. Rails uses a “push” model which means that
the controller sets the variables for its views. In a “pull” model, a
view is central and determines which variables to slurp from which
controllers.

Now in the Rails push model when you access or edit a view that does not
truly belong to that controller, it is easy to forget about variables
and stuff may break. Also things will be less than transparent when you
try to track which variable is set where and why it contains the value
that it does. It just doesn’t match up with the 1:N controller-views
push model.

So yeah, it definitely doesn’t suit the Rails way of doing things. You
should use a shared view template or perform a redirection.


Roderick van Domburg
http://www.nedforce.com

On 9/19/07, Dave S. [email protected] wrote:

methods for dealing with Comments
OK, no problem. So far so good.

If the Foo page has a form that allows a comment to be added and invokes
CommentsAjaxController#add_comment, how can I get that remote method to
render views/foo/add_comment.rjs?

class CommentsAjaxController
def add_comment
render :template => ‘foo/add_comment’, :layout => false
end
end

Or–in the general case–how can I
get the view conventions to work the way they always do, but to have it
look for the correct view belonging to an originating controller rather
than the invoked controller?

It sounds like you want to share the add_comment action across several
controllers, but have different templates for each. If that’s the
case, the simplest solution is to just throw the add_comment action
into ApplicationController. Or, for a finer-grained approach, put the
add_comment method into a module and mix in the module to those
controllers that need it. In that way, you get a shared action, but
separate views.

Thanks for the advice guys and gals.

The problem is that I’m working on my first AJAX-heavy (and I mean
HEAVY) site. It behaves almost more like a desktop app than a webapp.

It’s actually an AssetManagement app. The page for managing assets has
to hit remote functions for adding/removing comments, adding/removing
tags, adding/removing asset associations, editing/creating/deleting
assets, refreshing/sorting the list, etc.

In my particular case–at least at the moment–I don’t NEED to have a
reusable controller for those things like tags or comments. My problem
is simply that the controller was getting awfully messy with a couple of
core views, and a gazillion remote functions. I wanted to separate out
those remote functions in a way that was organized (tags, comments, etc)
and hopefully allowed for reusability.

So shared views is pretty much the answer, then? Is it possible to tell
it to render the originating controller’s view template for a particular
function name but to treat it like a “shared view” (and have access to
remote controllers data)? Is that what “render :template
‘controller/view’” does?