The platform's central abstraction, in one line: an input type defines what an editor can enter, a layout area binds one input type to a named slot, a layout groups areas and points at a template, a record or component holds the content for each area, and the template reads it back. Read this book straight after the template languages.
L1–L3
Unit 07 · The shorthand, and working with each input type
Text, select and switch areas in templates
The shorthand, and working with each input typePage 28 of 45Updated
What text, textarea and rich text areas store and how to escape each; reading one select key or many and mapping them onto CSS classes; and why a switch is a boolean through the shorthand but the string 1 through getAreaText().
Who this is for. Developers rendering text-category areas, and content modellers choosing which editor an author gets. After this page you can print each of them safely in both engines and test a switch without surprises.
All three store text, and getAreaText() returns it as stored. Nothing an author enters is escaped on the way out, in either engine: where escaping matters, the template does it.
Editor
Stores
Give it to authors for
Print it with
Text
One line of text
Headings, labels, button text
The shorthand
Textarea
Several lines of plain text; line breaks stay line breaks and are never turned into <br>
A short plain summary, an address
getAreaText(), escaped, with line breaks converted
Rich text
HTML from the editor
Formatted body copy with links and lists
The shorthand, unescaped: escaping would print the tags
The textarea uses the explicit getter because it is escaped: escaping the shorthand would also escape its live-preview wrapper, which then shows as code in the preview. In Smarty nl2br() is written as a function, because the security policy allows PHP functions but not PHP modifiers.
Output also depends on the editor in two ways:
Every text editor: images from the file manager are kept in the stored text as placeholders, and output turns them back into image markup.
Rich text only: a link to another record, inserted through the editor, is rewritten on output to that record's current address. A plain text or textarea area prints a link exactly as typed.
Select and multiselect
A select offers a fixed list of options, each defined on the input type as key::Label. Authors see the label; the key is stored, and getAreaText() returns it.
A multiselect stores several keys, as a JSON list. Read them with getAreaValues(), which returns the keys as an array; the shorthand would give the stored text, not a list.
Map keys straight onto CSS classes, so the stylesheet does the branching rather than the template. The keys go into attributes, so they are read with the explicit getters:
Template code
{* select "size": sm, md or lg *}
<a class="button button--{$component->getAreaText('size')}" href="{$component->getAreaText('ctaUrl')}">{$component->linkName}</a>
{* multiselect "hideOn": any of mobile, tablet, desktop *}
<div class="block{foreach $component->getAreaValues('hideOn') as $device} hide-{$device}{/foreach}">
{# select "size": sm, md or lg #}
<a class="button button--{{ component.getAreaText('size') }}" href="{{ component.getAreaText('ctaUrl') }}">{{ component.linkName }}</a>
{# multiselect "hideOn": any of mobile, tablet, desktop #}
<div class="block{% for device in component.getAreaValues('hideOn') %} hide-{{ device }}{% endfor %}">
The pattern scales to a whole component with a shared include: hand it a list of select areas, whose keys become classes, and a list of switch areas, whose names become classes when they are on. A new option is then a new CSS rule, not a template change.
Because the key is what you style on, choose keys that make good class names, and never change a key once content exists. Changing a label is safe; changing a key leaves every stored value pointing at an option that no longer exists. When the page needs the label rather than the key, the area's input type holds the options: getAreaType('size')->getPossibleValues().
Switch
A switch stores the text 1 when on and 0 when off. The admin form always sends one or the other, so a switch that has been saved is never empty. An area that has never been saved, such as one added to the layout after the content was written, has no value at all, and getAreaText() returns null.
Read
On
Off
Never saved
The shorthand, {$component->hideDate}
true
false
false
getAreaText('hideDate')
'1'
'0'
null
Template code
{* the shorthand: a real boolean *}
{if $component->hideDate}...{/if}
{* the getter: compare with the string *}
{if $component->getAreaText('hideDate') == '1'}...{/if}
{# the shorthand: a real boolean #}
{% if component.hideDate %}...{% endif %}
{# the getter: compare with the string #}
{% if component.getAreaText('hideDate') == '1' %}...{% endif %}
The string is where people get caught. Inside the template, '0' is false in both engines, so a bare test on the getter happens to work. Outside it, it does not: printed into a data- attribute for a script, '0' is a non-empty string, which JavaScript treats as true. Print an explicit value instead. The switch shorthand is safe inside the attribute, because a boolean is never wrapped for live preview:
Through the shorthand, off and never saved are both false, so name a switch for the behaviour that is not the default: hideDate, not showDate. An area nobody has touched then gives the default.
Reading an area with the wrong getter
getAreaText() on an area that is not a text area does not fail. It returns a sentence, Error: cannot get text content for area of type followed by the area's type and category, and that sentence then appears on the page. If it shows up, the template is using the text getter on an image, file, record or component area: use the shorthand or the matching getter instead.
Dates, numbers, colours and code have the next page; files, records and component areas the one after.