statamic / ideas

đŸ’¡Discussions on ideas and feature requests for Statamic
https://statamic.dev
32 stars 1 forks source link

Dynamic Antlers Variables #907

Closed JohnathonKoster closed 1 year ago

JohnathonKoster commented 1 year ago

This idea is to make it a bit easier to work with dynamic variable names in Antlers; this would help those who are working with highly reusable partials/components and want to make variables dynamic depending on prefixed field-sets, different scopes, etc.

I have a working proof-of-concept for the proposed additions but want to gather Core-team/community feedback before putting in the extra work to polish it up and make it PR-ready.

Currently Available Solutions

There exist two ways of working with dynamic variables directly in Antlers:

Prefix handles are great for working with prefixed field-sets, but only work with prefixes, and also only on the partial and scope tag:

{{
    title = 'A non prefixed title';
    the_title = 'The prefixed title';
}}

{{# A non prefixed title #}}
{{ title }}

{{ scope handle_prefix="the_" }}
    {{# The prefixed title #}}
    {{ title }}
{{ /scope }}

{{# A non prefixed title #}}
{{ title }}

Constructing dynamic variables using the "@" variable syntax is also possible, but they also become very difficult to reason with if you need to use them in multiple places:

{{
    the_variable = 'Hello, wilderness';
    another_variable = 'the_variable';
}}

{{# Hello, wilderness #}}
{{ @another_variable }}

Proposed Addition: Dynamic Variable Syntax

The main idea is to make the syntax that most people reach for "just work", with a few caveats.

The most common syntax I've encountered when seeing questions related to dynamic variables is something along the lines of:


{{ the_{{ type }} }}

For performance reasons (and to help the parser out), this proposal would require all dynamic variables to be prefixed with $$:

{{
    the_text = 'The text';
    the_image = 'The image';
    the_label = 'The label';
    types = [
        ['type' => 'text'],
        ['type' => 'image'],
        ['type' => 'label'],
    ];
}}

{{ types }}
    {{ $$the_{type}  }}
{{ /types }}

This would produce the result:

The text
The image
The label

This new variable type uses the double dollar-sign syntax to indicate that the variable name is dynamic, and that Antlers should resolve its value before continuing. This also means this dynamic syntax will not work to construct dynamic tag names by design.

In the example above Antlers will detect that a nested {type} variable exists within the variable name and use it to construct the final variable name. These dynamic variables can also be used with modifiers, just like any other variable:

{{ types }}
    {{ $$the_{type} | upper }}
{{ /types }}

These dynamic variables can even be used as a tag-pair when resolving the names of arrays/other loopable values:

{{
    my_variable = 'colors';
    the_colors = [
        'red', 'green', 'blue'
    ];
}}

{{ $$the_{my_variable} }}
    {{ value }}
{{ /$$the_{my_variable} }}

which returns:

red
green
blue

This syntax is not limited to a single interpolated value, but it is recommended that template authors keep the number of dynamic values low to help readability:

{{ $$the_{variable}_{name} }}

This rules for interpolated values are the same as all other places in Antlers. This allows us to conditionally add the _ character in variable names:

{{
    the = 'Just The';
    the_text = 'The text';
    the_label = 'The label';
    types = [
        ['type' => 'text'],
        ['type' => null],
        ['type' => 'label'],
    ];
}}

{{ types }}
    {{ $$the{type ?= '_{type}'} }}
{{ /types }}

Which produces the following output:

The text
Just The
The label

Proposed Addition: Explicit Language-Assisted Variable Existence Checks

This addition would make it possible to check if any given variable actually exists before attempting to use it. This is implemented as a language-level operator, and looks like this:

{{ if exists ('title') }}
    Yes.
{{ /if }}

This operator also supports checking if multiple named variables exist by supplying an array. In the following, it will only return true if all three variables actually exist (null values are fine, just as long as they exist):

{{ if exists (['title', 'meta_description', 'og_image']) }}
    Yes.
{{ /if }}
JohnathonKoster commented 1 year ago

Closing due to a simpler solution in the works