Calling a controller/action from another controller

Hi,

Is there a way to call a controller/action pair from another controller
as well as render the view for that pair? I have tried using “render
‘ctrl/action’” and that works, but the controller code isn’t called
(only the view is rendered). A redirect_to isn’t desirable either as I
want the resulting content to be rendered in the current controller’s
layout.

I have thought of a workaround, but it seems to violate some design
principles of Rails. That is, the put the controller code for the
actions that I need to do this with in helpers and let the action
essentially call the controller. However, this doesn’t seem like a very
elegant solution. Is there a better way?

Thanks,
Jonathan

P.S. I thought I read a topic along these lines before, but I can’t
seem to locate it now.

On Feb 16, 2006, at 9:15 AM, Jonathan Leonard wrote:

P.S. I thought I read a topic along these lines before, but I can’t
seem to locate it now.

Use render_component:

<%= render_component :controller => ‘foo’, :action => ‘bar’ %>

Cheers-
-Ezra Z.
Yakima Herald-Republic
WebMaster

509-577-7732
[email protected]

Ezra Z. wrote:

On Feb 16, 2006, at 9:15 AM, Jonathan Leonard wrote:

P.S. I thought I read a topic along these lines before, but I can’t
seem to locate it now.

Use render_component:

<%= render_component :controller => ‘foo’, :action => ‘bar’ %>

Thanks for the response.

But, this looks like view code. I’m trying to do this from the
controller. (The reason I can’t use the view is I have an action called
‘menu’ in my controller which handles menu hits (it can be one of many
different actions).

I tried calling render_component from the controller, but that was
basically the same as a redirect_to (i.e., it didn’t preserve the
current controller’s layout. I even tried adding :layout =>
‘currentController’ to the render_component call. Am I missing
something?

–J

On Feb 16, 2006, at 10:34 AM, Jonathan Leonard wrote:

I tried calling render_component from the controller, but that was
basically the same as a redirect_to (i.e., it didn’t preserve the
current controller’s layout. I even tried adding :layout =>
‘currentController’ to the render_component call. Am I missing
something?

–J

Yes you need to set a flag in the render component call to tell the
other controllers action to render without layout:

render_component :controller => ‘foo’, :action → ‘bar’, :no_layout
=> true

Then in the other controller action you check for the flag and render
without a layout if its present:

def bar

do stuff you need

if params[:no_layout]
render :layout => false && return
end

do the normal render however you like

end

Hope that helps

-Ezra Z.
Yakima Herald-Republic
WebMaster

509-577-7732
[email protected]

On Feb 16, 2006, at 2:16 PM, Jonathan Leonard wrote:

I think you meant:

render_component :controller => ‘foo’, :action => ‘bar’, :params => {
“no_layout” => true }

Ahh yes, you are right. I was just typing it and didn’t test anything :wink:

end
proper
layout (i.e., the calling controller’s layout).

–J

What exactly are you wanting to do? I guess I don’t understand. If
you want to render the action from another controller in the current
controller/actions view and layout you do need layout false in the
component that gets rendered.

-Ezra Z.
Yakima Herald-Republic
WebMaster

509-577-7732
[email protected]

Ezra Z. wrote:

Yes you need to set a flag in the render component call to tell the
other controllers action to render without layout:

render_component :controller => ‘foo’, :action -> ‘bar’, :no_layout
=> true

I think you meant:

render_component :controller => ‘foo’, :action => ‘bar’, :params => {
“no_layout” => true }

Then in the other controller action you check for the flag and render
without a layout if its present:

def bar

do stuff you need

if params[:no_layout]
render :layout => false && return
end

do the normal render however you like

end

Ok. This actually worked (but didn’t produce the intended result). It
just removed all formatting (i.e., style).

So, I changed the above code slightly to be :params => { ‘layout’ =>
‘theDesiredLayout’ } and set :layout = params[‘layout’] in the call to
render. This brought back the style that was there previously (the
scaffold stylesheet), but still didn’t display the content in the proper
layout (i.e., the calling controller’s layout).

–J

Jonathan Leonard wrote:

I know that won’t work because the action is an instance method instead
of a class method. Is there an instance of each controller lying around
somewhere?

I just thought of something that should work. I could put the
functionality that’s currently in the called controller in
application.rb and call it from both the calling controller as well as
the called controller and use the render ‘ctrlr/action’ approach that
works. Is this the best way?

Thanks,
Jonathan

On Feb 16, 2006, at 3:44 PM, Jonathan Leonard wrote:

I just thought of something that should work. I could put the
functionality that’s currently in the called controller in
application.rb and call it from both the calling controller as well as
the called controller and use the render ‘ctrlr/action’ approach that
works. Is this the best way?

Thanks,
Jonathan

Well it might not belong in the controller at all. I usually use
render_component in my views to encapsulate sidebars and other
sections from other controllers into the view. But in any regard
render_component shouldn’t take the layout => false , that goes in
the other controller/action being called. you need to conditionally
decide to render without layout so you just get the content and view
to render into your current state.

But yeah you can refactor shared actions into your application

controller if that works in this particular case.

-Ezra Z.
Yakima Herald-Republic
WebMaster

509-577-7732
[email protected]

Ezra Z. wrote:

Well it might not belong in the controller at all. I usually use
render_component in my views to encapsulate sidebars and other
sections from other controllers into the view. But in any regard
render_component shouldn’t take the layout => false , that goes in
the other controller/action being called. you need to conditionally
decide to render without layout so you just get the content and view
to render into your current state.

Oops, I meant to say ‘render’ there instead of ‘render_component.’ That
is what I was actually doing and it still didn’t get the current layout.

But yeah you can refactor shared actions into your application
controller if that works in this particular case.

Yea, that makes sense.

Thanks for the help.
Jonathan

Ezra Z. wrote:

What exactly are you wanting to do? I guess I don’t understand. If
you want to render the action from another controller in the current
controller/actions view and layout you do need layout false in the
component that gets rendered.

Yea, that’s it. I want to display the result of another
controller/action in the current controller’s layout/view. I’ve tried
render_component with :layout => false and :layout => ‘callingCtrl’.
:layout => false yielded a page with no style at all (as expected) and
:layout => ‘callingCtrl’ yielded the same page I would’ve gotten from a
redirect_to (except the address remained in the calling controller) or a
render_component with no layout clause.

BTW, doing this:

render ‘ctrlr/action’

renders the page embedded in the current layout like I want, but that
doesn’t actually call the controller code. So, if I wanted to use this
approach, I’d either have to duplicate the controller code in the
calling controller or put that code in a helper and call it from the
view (or is there another way?? such as a controller helper, or a way to
directly call something like this:

class CallingController
def foo
CalledController.action
render ‘ctrlr/action’
end
end

I know that won’t work because the action is an instance method instead
of a class method. Is there an instance of each controller lying around
somewhere?

–Jonathan