Writing and using METAL macros
ZPT METAL attributes are used to define and use macros. Macros are reusable trees of markup - a section of markup (which can include elements and text), contained within a single element (the root element of the macro).
METAL macros are usually defined in separate source files to the documents which make use of them but they may be used within the same source file in which they are defined if you wish.
Macros replace their caller
METAL macros are defined/created using the metal:define-macro
attribute.
They are used with the metal:use-macro
attribute.
When a macro is used, the macro and its content are copied and inserted at the point of usage, replacing the original element. Here is the source code for a simple macro:
<footer metal:define-macro="standard_footer">
<p class="copyright_statement">
All content copyright © 2017 Nichola Nicholson
</p>
<p class="report_error_link">
Please report errors and omissions to
<a href="mailto:errors@example.com">errors@example.com</a>.
</p>
</footer>
Here is a snippet of a page which uses the macro we defined above.
<body>
<section>
Here's the page content
</section>
<div metal:use-macro="here/Documents/Shared/footers/macros/standard_footer">
The footer goes here
</div>
</body>
The expression here/Documents/Shared/footers/macros/standard_footer
is an example of a
TALES expression.
If you have not yet learned TALES then do not worry, it is a sample of a reference to the
standard_footer macro we defined in the first snippet.
Now let's see what would happen if the document above were rendered via ZPT-Sharp:
<body>
<section>
Here's the page content
</section>
<footer>
<p class="copyright_statement">
All content copyright © 2017 Nichola Nicholson
</p>
<p class="report_error_link">
Please report errors and omissions to
<a href="mailto:errors@example.com">errors@example.com</a>.
</p>
</footer>
</body>
Note how the <div>
element which appears in the second snippet has been discarded
in the rendering process, along with all of its content.
The element which carries the metal:use-macro
attribute, along with its contents, are replaced
by the incoming macro.
Slots inject content into macros
Macros are more useful when you can customise them; one way of doing this is to use slots.
Defining a slot inside a macro's markup - using the metal:define-slot
attribute - creates
a placeholder in the macro.
Macros may contain as many or as few slots as you wish.
When a macro is used, any number of metal:fill-slot
attributes may be used inside the
metal:use-macro
markup.
When the document is rendered, elements which fill slots (along with their content) replace the
corresponding slot definition in the macro.
Let's look at an example:
<!-- ZPT source for a macro with slots -->
<div metal:define-macro="slot_example">
<p>This is static content in the macro.</p>
<p metal:define-slot="slot_one">This is slot one content from the macro.</p>
<p metal:define-slot="slot_two">This is slot two content from the macro.</p>
</div>
<!-- ZPT source for something which uses that macro -->
<section metal:use-macro="slot_example">
<p>This won't be rendered</p>
<div metal:fill-slot="slot_two">
This is some <strong>slot two filler</strong> from the document.
</div>
<p>This won't be rendered either</p>
</section>
Now let's take a look at how that ZPT source (which uses the macro) would be rendered. Here is the expected result:
<div>
<p>This is static content in the macro.</p>
<p>This is slot one content from the macro.</p>
<div>
This is some <strong>slot two filler</strong> from the document.
</div>
</div>
There are a few things to notice here:
- Just like macros, the slot-filler element replaces the whole slot-definition element upon rendering. This includes all of the element content too.
- Slots do not have to be filled. Just like slot_one in the example above, if a slot is defined and no filler is provided then the slot content in the source macro will remain.
-
Other markup which is contained within the element which carries the
metal:use-macro
attribute, which is outside of any slot fillers, will be discarded.
Macro extension
An advanced topic is that of METAL macro extension using the metal:extend-macro
attribute.
Developers might recognise this concept as akin to subclassing a METAL macro.
There is separate reference for METAL macro extension but in summary it is useful for creating a macro which is based upon another macro.
-
The extended macro (the extension) is usable via
metal:use-macro
just like any other. - The extension may optionally fill slots from the base macro.
- Where the extension does fill slots, it may optionally define any number of new slots.
- Where the extension fills a named slot, it may optionally define that same slot name again.
List of METAL attributes
Here is a complete list of the available METAL attributes, along with links to their detailled reference.
Attribute | Summary |
---|---|
define-macro |
Define a METAL macro consisting of the current element and all of its children, providing a macro name |
use-macro |
Replace the current element and all of its children/content with the specified macro |
define-slot |
Define a slot within a macro, which acts as an optional extension point when the macro is used |
fill-slot |
When using a macro, replace the named slot with the specified markup |
extend-macro |
Used alongside define-macro to create a new macro based upon an existing one, extending and/or redefining its slots |