Search code examples
craftcms

How to create a dropdown in Twig using the syntax in the manual?


I'm trying to follow the syntax in the docs: https://github.com/craftcms/contact-form/blob/v2/README.md to output a select. I tried all sorts of syntax but I couldn't get it right… 😅

From the docs, I would have thought this would work, but it just adds the options as an attribute of the select field in the HTML.

{{
  tag(
    'select',
    {
      id: 'type',
      name: 'message[type]',
      options: [
        {
          label: 'option1',
          value: 'option1'
        }
      ],
      class: message and message.hasErrors('message.type')
        ? 'error'
    }
  )
}}

The HTML output I get from that:

<select id="type" class="" name="message[type]" options="[{...;}]"></select>

I know I could just code it up as "html" but I'd like the keep the markup consistent and it makes the validation cleaner. Any pointer in the right direction much appreciated!


Solution

  • What I think you want.

    I understand that you want to produce a dropdown selection using the tag() function, and you want to avoid just slapping in some HTML and calling it a day.

    The HTML we want out of this...

    A <select> tags options should be inside the element, as <option>s.
    So the output we want is.

    <select id="type" class="" name="message[type]">
       <option value="option1">Option1</option>
       <option value="option2">Option2</option>
    </select>
    

    tag() function vs the {% tag %} tag

    We have two options for making a tag in Craft-Twig. The tag() function and the {% tag %} tag.

    While they give use two routes to the same end...

    • tag() is better for when the tag has no innerHTML/innerText, or the contents are pulled from another function or API.
    • {% tag %} is better for when the tag has longer content, or that content is being dynamically generated.

    I think {% tag %} is the better option for this situation, but I'll go through them both.

    The tag() function

    Documentation: Craft CMS - Functions - tag()

    tag('tag_type',{options_object})
    The tag_type is the <tag_type>. Everything else is optional.

    The options_block may include two options that affect the inner contents of a tag:

    • text: "Woo!": Text will be HTML encoded and rendered inside your tag.
    • html: "<i>Yay!</i>": HTML to be slapped into your tag, with no safety-net.

    Everything else is stringified and added as an attribute.
    So id: "thinger becomes <tag id="thinger">

    Why is your code doing this?

    option={...} isn't one of tag()'s two 'inner stuff' options, so it's just turned into a string and slapped in as an attribute.

    To get what you want with tag()

    Just add your desired innerHtml as a string to the options_object's html key.

    {{
      tag(
        'select',
        {
          html:"<option value="Option1">Option1</option><option value="Option2">Option2</option>"
        }
      )
    }}
    

    As you can see, though, that can be a bit cumbersome when you have long HTML to insert.

    The {% tag %} tag

    Documentation Craft CMS - tags -tag

    {% tag %} works almost exactly like tag(), except it let's us put the 's contents inside {% tag %} and {% endtag %}.

    It accepts one argument, the tag type ('element' below), and an optional object via with, with each key/value pair becoming attributes on the tag to create.

    {% tag 'element' with {
       class: "some class"
       custom_attribute: "some value"
     }
    %}
       Your html {{ 'and_Twig()'|upper }} here.
    {% endtag %}
    

    becomes

    <element class="some class" custom_attribute="some value">Your html AND TWIG here.</element>
    

    This is better suited for when you have verbose tag contents, or contents that are dynamically generated by other tags/functions.

    To get what you want with {% tag %}

    Just put your option tags inside the {% tag %}...{% endtag %}

    {% tag 'select' with {
       id: 'type',
       name: 'message[type]',
     -%}
       <option value="option1">Option1</option>
       <option value="option2">Option2</option>
    {%- endtag %}
    

    But I don't want to HTML it up...

    No problem!

    {% tag 'select' with {
       id: 'type',
       name: 'message[type]',
     -%}
       {% tag('option', {
          text: "Option1"
          value: "option1"
       }) %}
       {% tag('option', {
          text: "Option2"
          value: "option2"
       }) %}
    {%- endtag %}