ruby-on-railsruby-on-rails-7

How do I pass local variables to view layouts in Rails 7?


I have a view that I'm rendering like this:

action_controller.render_to_string(
    layout: 'document',
    template: 'administrator/users/export',
    encoding: 'UTF-8',
    formats: [:pdf],
    handlers: [:erb],
    locals: {
      testvar: "test"
    }
  )

And I'm trying to use this testvar variable in the specified document layout:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html class="document">
<head>
  <title>
    <%= "Test: #{testvar}" %>
  </title>
  <meta charset="utf-8"/>
  <meta name="viewport" content="width=device-width,initial-scale=1">
</head>
<body class="document pdf">
<%= yield %>
</body>
</html>

But I'm getting this exception:

*** NameError Exception: undefined local variable or method `testvar' for #ActionView::Base:0x000000000142f8

I also cannot get any instance variables in the layout.

How do I do this in Rails 7? This was possible to do in Rails 6.


Solution

  • The issue isn't really how you're passing the variables - it's how you're accessing them.

    You can access locals through local_assigns:

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html class="document">
    <head>
      <title>
        Test: <%= local_assigns[:testvar] %>
      </title>
      <meta charset="utf-8"/>
      <meta name="viewport" content="width=device-width,initial-scale=1">
    </head>
    <body class="document pdf">
    <%= yield %>
    </body>
    </html>
    

    But for some obscure reason the layout doesn't have access to the actual local variables like the view does. You can see this if you put a breakpoint in the layout:

    (ruby) local_assigns
    {:title=>"Hello World"}
    (ruby) defined?(title)
    nil
    

    Wat duck purely for humor

    This suprising behavior is also documented in a stale issue.