Rails: Render a View (not a partial) From Within a View

35,574

Solution 1

Rendering a non-partial view inside another view isn't exactly the Rails Way™. Your current solution is probably better, and not an uncommon approach. Rename it _body, or something else appropriate, if you feel weird about the the partial name being the same as the action.

However if your view can be shared, as it seems like it could in this case, you could just make it a layout.

This is facilitated by the fact that, somewhat against the principle of least surprise, Rails will render an html template for a js action if no js template exists. This means that you could remove both the js template, and the partial, and just create a layout entitled, for example, fadein.js.erb:

# yourviews/show.html.erb
<div>Content!</div>

# layouts/fadein.js.erb
$("#main").fadeIn("<%= escape_javascript(yield) %>");

# YourController.rb
def show
  # ...
  respond_to do |wants|
    wants.html
    wants.js { render :layout => "fadein" }
  end
end

Solution 2

Like others have mentioned, rendering another non-partial view in another view is "Not the rails way", if however you still insist on doing it, one method is:

<%= render :file => '<replace with relative/absolute path>' %>

Solution 3

This is not a good practice as you can see by the comments. Rails have the concepts of view, partial and layout. That said, the view is the only one you should keep using only once. So my suggestions are:

  1. If you feel that more than one extra view could be rendered inside your current view, you are most likely looking for a layout
  2. If you feel that one of your views should be rendered in many pages, you are looking for a partial
  3. If this view should render only one extra view inside it, and that view should only be rendered inside the current view, you can pick any of the above or none of it - that's it, go with a single file

Solution 4

you just need to pass a context to your render method:

<%= render :template => "show" -%>
Share:
35,574
nullnullnull
Author by

nullnullnull

Updated on July 09, 2022

Comments

  • nullnullnull
    nullnullnull almost 2 years

    I have a controller that responds to both html and js. The html view renders the whole page (including the header and footer), while the js only replaces #main. Aside from the header and footer, both formats render the same content. I can get this effect with three files:

    _show.html.erb
    <div>Content!</div>
    
    show.html.erb
    <%= render "show" %>
    
    show.js.erb
    $("#main").fadeIn("<%= escape_javascript(render 'show') %>");
    

    This works, but I'd prefer if I didn't need a separate _show partial. Unfortunately, this doesn't work:

    show.html.erb
    <div>Content!</div>
    
    show.js.erb
    $("#main").fadeIn("<%= escape_javascript(render 'show') %>");
    

    As Rails will look for the show partial, not the actual view.

    Is there a way to get Rails to look for the view file, rather than a partial?

  • nullnullnull
    nullnullnull about 11 years
    Thanks a million. This solution boils three files (show.html.erb, show.js.erb, and _show.html.erb) into one show.html.erb. I can apply it across models and actions, and there'll still only need to be one layouts/fadein.js.erb file. Brilliant!
  • Kopty
    Kopty almost 11 years
    This worked really well! Thank you so much. I just had a question, I couldn't get this to work for a "create" scenario where a form submission should redirect me to the record which I just created. I have added remote: true in my form_for, but it searches for the create template even though I have done the respond_to for JS to point to the layout JS file. Any tips? Thanks.
  • numbers1311407
    numbers1311407 almost 11 years
    @Kopty It's still rendering the template, as HTML, inside the javascript layout. So it's still going to look for a create template as usual unless you tell it not to. If you wanted to render the show template, you could do something like render :show, :layout => "fadein"
  • Kopty
    Kopty almost 11 years
    Thanks for the tip numbers. I tried that and am getting the error "':show' is not an ActiveModel-compatible object. It must implement :to_partial_path." I also tried hard-coding the controller's show action but that didn't work either.