Home > front end >  rendering partial does not render the layout
rendering partial does not render the layout

Time:07-09

I have an action show that has a correspondent show.html.erb file as well. What I'm trying to do is add an "early return" so to say in case the record doesn't exist:

class MessagesController < ApplicationController
  def show
    unless Message.exists?(mid: params[:id])
      return render partial: 'messages/expired'
    end

    # rendered by views/messages/show.html.erb
  end
...

In case the message exists, this works fine and I can see rails rendering all the files/layouts:

19:51:19 web.1  |   Message Exists? (0.3ms)  SELECT 1 AS one FROM "messages" WHERE "messages"."mid" = $1 LIMIT $2  [["mid", "01G7FD3BBGE2EJH3ABVC5RRWAG"], ["LIMIT", 1]]
19:51:19 web.1  |   ↳ app/controllers/messages_controller.rb:3:in `show'
19:51:19 web.1  |   Rendering layout layouts/application.html.erb
19:51:19 web.1  |   Rendering messages/show.html.erb within layouts/application
19:51:19 web.1  |   Rendered messages/show.html.erb within layouts/application (Duration: 1.9ms | Allocations: 1159)
19:51:19 web.1  |   Rendered layouts/_nav.html.erb (Duration: 0.4ms | Allocations: 129)
19:51:19 web.1  |   Rendered layout layouts/application.html.erb (Duration: 82.3ms | Allocations: 26506)
19:51:19 web.1  | Completed 200 OK in 94ms (Views: 88.6ms | ActiveRecord: 2.6ms | Allocations: 29578)

but here is the weird thing: in case the record doesn't exist, Rails does render the partial, but only the partial:

19:52:56 web.1  |   Message Exists? (0.6ms)  SELECT 1 AS one FROM "messages" WHERE "messages"."mid" = $1 LIMIT $2  [["mid", "01G7FD3BBGE2EJH3ABVC5RRWA"], ["LIMIT", 1]]
19:52:56 web.1  |   ↳ app/controllers/messages_controller.rb:3:in `show'
19:52:56 web.1  |   Rendered messages/_expired.html.erb (Duration: 0.1ms | Allocations: 22)
19:52:56 web.1  | Completed 200 OK in 10ms (Views: 1.3ms | ActiveRecord: 0.6ms | Allocations: 885)
19:52:56 web.1  |

I don't remember seeing this behavior before so I'm really confused...what am I doing wrong? Honestly, I'm a little burned out so it could be I can't see something that is right in my face but I simply can't understand why is this happening...

Interestingly, if do render 'messages/expired', without the partial: and rename the file to remove the underscore so it is not a partial anymore, it works:

19:56:04 web.1  |   ↳ app/controllers/messages_controller.rb:3:in `show'
19:56:04 web.1  |   Rendering layout layouts/application.html.erb
19:56:04 web.1  |   Rendering messages/expired.html.erb within layouts/application
19:56:04 web.1  |   Rendered messages/expired.html.erb within layouts/application (Duration: 0.1ms | Allocations: 141)
19:56:04 web.1  |   Rendered layouts/_nav.html.erb (Duration: 0.4ms | Allocations: 124)
19:56:04 web.1  |   Rendered layout layouts/application.html.erb (Duration: 2.6ms | Allocations: 2627)
19:56:04 web.1  | Completed 200 OK in 7ms (Views: 3.4ms | ActiveRecord: 2.1ms | Allocations: 5494)

wild guess: it has something to do with the fact that all of that is happening on show so some Rails' default kicks in.

CodePudding user response:

render method in controllers processes its arguments a bit differently than in views.

# NOTE: :body, :plain, :html, :inline, :partial
#       will render without a layout
#       unless :layout option is specified
#       https://github.com/rails/rails/blob/v7.0.3/actionview/lib/action_view/layouts.rb#L431

def show
  # Without layout
  render partial: "expired"           # renders "messages/_expired"

  # With layout
  render "expired"                    # renders "messages/expired"
  render template: "messages/expired" # renders "messages/expired"
  render "_expired"                   # renders "messages/_expired"

  # Doesn't work
  render partial: "expired",          # renders "messages/_expired"
    layout: "application"             # in "layouts/_application" # <= doesn't exist
end

In the view:

# Without layout
<%= render "expired" %>                      # renders "messages/_expired"
<%= render partial: "expired" %>             # renders "messages/_expired"
<%= render template: "messages/_expired" %>  # renders "messages/_expired"
<%= render template: "messages/expired" %>   # renders "messages/expired"


# With layout
<%= render template: "messages/expired", layout: "layouts/application" %>
                                             # renders "messages/expired"
                                             # in "application" layout (again)
<%= render partial: "expired", layout: "application" %>
                                             # renders "messages/_expired"
                                             # in "messages/_application" partial layout

You can keep _expired partial or rename it to expired and just use the appropriate render call.


https://api.rubyonrails.org/classes/AbstractController/Rendering.html#method-i-render

https://api.rubyonrails.org/classes/ActionView/Helpers/RenderingHelper.html#method-i-render

  • Related