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