How to postpone <head> rendering?
Asked Answered
S

1

3

I have the layout like this:

@@ layouts/v2.html.ep
<html lang="en">
  <head>
    %= content_for 'stylesheets'
  </head>
<body>
  %= include 'layouts/v2/header'
  <main class="main">
    %= include 'layouts/v2/menu'
    <div class="content">
    %= content
    </div>
  </main>
</body>
</html>


@@ layouts/v2/menu
% content_for stylesheets => begin
  %= stylesheet 'v2/css/menu.css'
% end
<aside class="menu">
  ...
</aside>

@@ layouts/v2/header
% content_for stylesheets => begin
  %= stylesheet 'v2/css/header.css'
% end
<header class="header">
  ...
</header>

Here when templates are included I include their style sheets. Notice this in template:

% content_for stylesheets => begin
  %= stylesheet 'v2/css/menu.css'
% end

But this is too late to do that, because the <head> is already rendered.

As workaround this problem I can move %= content_for 'stylesheets' from <head> to the bottom of page. But I want style sheets are loaded first.

Is there any way to postpone rendering for the content of 'stylesheets' block until whole page is rendered?

UPD

Thank to @amon about that Mojolicous layouts are rendered inside out. I understand the problem and there for templates included from first layout I include stylesheets manually:

@@ layouts/v2.html.ep
<html lang="en">
  <head>
    %= stylesheet 'v2/css/header.css'
    %= stylesheet 'v2/css/menu.css'
    %= content_for 'stylesheets'
  </head>
<body>
  %= include 'layouts/v2/header'
  <main class="main">
    %= include 'layouts/v2/menu'
    <div class="content">
    %= content
    </div>
  </main>
</body>
</html>

So in any rendered/included template (except layout) next works fine:

@@ some/template
% content_for stylesheets => begin
  %= stylesheet 'some/template.css'
% end
template content
Swifter answered 13/12, 2017 at 16:52 Comment(2)
The approach you'd use with TT: some_page.tt: [% stylesheet = ... %][% WRAPPER wrapper.tt %]...[% END %] and wrapper.tt: <head>[% stylesheet %]</head>[% content %]. Does Mojolicious have some kind of wrapper directive?Carnage
@ikegami: Seems it is layout helper commandSwifter
R
5

Mojolicous layouts are rendered inside-out, and you can nest arbitrarily many layouts.

Create a template that includes just the outermost document content and other HTML boilerplate, with a placeholder for the content of <body>

<html lang="en">
  <head>
    %= content_for 'stylesheets'
  </head>
  <body>
    %= content
  </body>
</html>

Then you can use that template as the layout for the body of your HTML page. I.e., use the layout helper like this:

% layout 'outermost_layout';
%= include 'layouts/v2/header'
<main class="main">
  %= include 'layouts/v2/menu'
  <div class="content">
  %= content
  </div>
</main>

After rendering the template, Mojolicious will check whether you specified a layout and then render it, using the output from this template as the outer layout's content. So because the innermost layout is rendered first, data can flow from your templates through the stash to wrapping layouts.

Rabush answered 13/12, 2017 at 17:3 Comment(4)
With your help I understand the problem I haveSwifter
@amon: "This allows data to flow from inner templates to outer layouts" I'm unsure that that's either true or relevantOstentation
@Ostentation Thank you for your style improvements (I'll come back later to edit relevant technical terms back in). I think mentioning the data flow is very relevant because that's at the core of the question: the data cannot flow back in time to where a block was already rendered, but it can flow to an outer layout.Rabush
@amon: You're welcome, but I don't think data flow is any more relevant to Mojolicious templates than it is to Perl subroutines, where you would never think to emphasise the concept.Ostentation

© 2022 - 2024 — McMap. All rights reserved.