Skip to content

Latest commit

 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 

readme.md

11 · Embedding Components

A layout for a piece of a page. The skeleton owns the markup, the caller owns the content - and you can use it three times on one page.

You will learn:

  • what {embed} does that {include} and {layout} cannot
  • how a component declares its holes and its parameters
  • why blocks inside an embed cannot collide with blocks outside it

Run it

php 11-embed/example.php

The idea

{layout} gives a whole page a skeleton. {include} drops a fragment in. Neither lets you say: here is a box with a fixed structure, and here is what goes inside it, right now, at this call site.

That is {embed} - include and layout in one tag.

{embed '@panel.latte', modifier: 'wide'}
	{block title}This month's picks{/block}
	{block body}
		<ul>...</ul>
	{/block}
{/embed}

The component is an ordinary template with blocks and parameters:

<section class="panel panel--{$modifier}">
	<h2 class="panel__title">{block title}{/block}</h2>
	<div class="panel__body">
		{block body}Nothing here yet.{/block}
	</div>
</section>

The example uses it three times on one page: a wide panel with a book list, a narrow one with opening hours, and a third that overrides only the title and inherits the default body. The class names, the heading level and the nesting live in exactly one file - change panel__body to something else and every panel on the site follows.

Why not just {include}?

You could pass the body in as a string parameter, and people do, and it is miserable: you end up building HTML in PHP, or escaping things by hand, or inventing a mini-language of flags (showFooter, titleTag, bodyClass) so the partial can decide what to render. {embed} lets the caller write plain markup in the place where it belongs.

The difference in one line: {include} passes values into a template, {embed} passes markup.

Isolation

Blocks inside an embed live in their own layer. Two panels on the same page can both define title without colliding, and neither collides with a title block belonging to the page's layout. This is the property that makes components composable at all - otherwise the third panel you add to a page would silently overwrite the first.

The isolation cuts both ways: inside an embed you can include blocks defined in the embed, blocks from the embedded template, and definitions from an {import} - but not the outer page's ordinary blocks. Nor does the embedded template see the caller's variables; it gets what you pass in the tag, which is the same discipline definitions have.

You can also embed a block instead of a file - {embed myComponent, ...} - handy when the skeleton is small enough to live next to its usage.

Output

<section class="panel panel--wide">
	<h2 class="panel__title">This month's picks</h2>
	<div class="panel__body">
			<ul>
		<li>It Works on My Machine by Marta Novak</li>
		<li>Escaping &amp; Other Life Skills by Petr Svoboda</li>
	</ul>
...
<section class="panel panel--narrow">
	<h2 class="panel__title">Opening hours</h2>
	<div class="panel__body">
			<p>Weekdays 9-18, Saturday 9-12.</p>
...
<section class="panel panel--narrow">
	<h2 class="panel__title">Newsletter</h2>
	<div class="panel__body">
		Nothing here yet.
	</div>
</section>

Three panels, one skeleton, and the third one shows the default body because it never overrode that block.

Try it yourself

  1. Add a {block footer} to @panel.latte with a default, then override it in one panel only.
  2. Give two panels a {block title} containing the same text and confirm they do not interfere - that is the isolation doing its job.
  3. Try {include title} from outside an embed. It is not visible there, and the error says so.

Further reading