April 22, 2022 —John Koster
A common design pattern when building Statamic sites is to use fieldsets to create reusable fields across different collections, forms, or pages. Additionally, it is also not uncommon to see specific partials created to display these fieldsets. This is nice since it allows for the same template code and groups of fields to be managed in a central location, and helps to keep things well organized and clean.
As an example, we could have the following fieldset to manage Frequently Asked Questions:
1title: FAQ 2fields: 3 - 4 handle: faq 5 field: 6 collapse: false 7 sets: 8 questions: 9 display: Questions10 fields:11 -12 handle: question13 field:14 input_type: text15 antlers: false16 display: Question17 type: text18 icon: text19 listable: hidden20 instructions_position: above21 read_only: false22 -23 handle: answer24 field:25 antlers: false26 display: Answer27 type: textarea28 icon: textarea29 listable: hidden30 instructions_position: above31 read_only: false32 display: Questions33 type: replicator34 icon: replicator35 listable: hidden36 instructions_position: above37 read_only: false
We could create a partial named components/faq.antlers.html
with the following template that would render our fieldset:
1<dl>2 {{ faq }}3 <dt>{{ question }}</dt>4 <dd>{{ answer }}</dd>5 {{ /faq }}6</dl>
and simply include the following in other templates when we wanted to reuse our Frequently Asked Questions template:
1{{ partial:components/faq }}
The previous example works great when field names do not change. However, because fieldsets can be prefixed, it becomes difficult to create truly reusable partials when targeting fieldsets (or when you may want to render the same group of fields multiple times on the same page, each with a different prefix). Let's assume that the previous fieldset was imported twice within the same blueprint. Once without a prefix, and a second time with the product_
prefix:
1title: Home 2sections: 3 main: 4 display: Main 5 fields: 6 - 7 handle: title 8 field: 9 type: text10 required: true11 character_limit: 012 display: Title13 validate:14 - required15 -16 import: faq17 -18 import: faq19 prefix: product_
Our page would now contain a faq
and product_faq
variable, each with their own list of Frequently Asked Questions. With this setup, we could either duplicate our partial or simply alias the single variable to render both:
1{{ partial:components/faq }}2 3{{ partial:components/faq :faq="product_faq" }}
This technique works well when the imported fieldsets are small (such as in this example), but can quickly become tedious if the imported fieldset contains many different fields. This often leads to attempts to writing invalid Antlers code like the following:
1{{# Inside `components/faq.antlers.html`. #}} 2<dl> 3 {{ {prefix}faq }} 4 <dt>{{ question }}</dt> 5 <dd>{{ answer }}</dd> 6 {{ /{prefix}faq }} 7</dl> 8 9{{# In another file. #}}10{{ partial:components/faq prefix="product_" }}
Even if the above had worked, it would make handling the non-prefixed case much more difficult. To help with this situation, Antlers Runtime supports the concept of forcing the Runtime to check if a variable exists with any given prefix, before falling back to the normal cascade rules.
To have the Runtime check if variables exist with a prefix, you simply need to add the handle_prefix
parameter to either the partial
tag or the scope
tag:
1{{ partial:components/faq }}2 3{{ partial:components/faq handle_prefix="product_" }}
When the Runtime encounters the handle_prefix
parameter, every variable will be checked to see if it exists with a prefix (but only within the partial or scope tag on which it was applied).
When the above code is being evaluated, it would be as if we had written this Antlers instead:
1<dl> 2 {{ faq }} 3 <dt>{{ question }}</dt> 4 <dd>{{ answer }}</dd> 5 {{ /faq }} 6</dl> 7 8<dl> 9 {{ product_faq }}10 <dt>{{ question }}</dt>11 <dd>{{ answer }}</dd>12 {{ /product_faq }}13</dl>
The first partial call would render like normal, because no prefix was specified. However, in the second partial call we are specifying the product_
prefix. Because a product_faq
variable does exist, that will be used instead of the faq
variable (if no prefixed match was found, Antlers will revert back to normal variable Cascade logic).
The scope
tag version might look something like this:
1{{ scope handle_prefix="product_" }}2<dl>3 {{ faq }}4 <dt>{{ question }}</dt>5 <dd>{{ answer }}</dd>6 {{ /faq }}7</dl>8{{ /scope }}
∎
The following amazing people help support this site and my open source projects ♥️
If you're interesting in supporting my work and want to show up on this list, check out my GitHub Sponsors Profile.