-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Add an html_attr function to make outputting HTML attributes easier
#3930
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: 3.x
Are you sure you want to change the base?
Changes from all commits
c53c6ab
b338e23
692ce55
6fe5e50
2d0166b
e49ac44
97825a8
0e001ef
4a9bddf
3ffa15e
80449f8
65b627f
22da58f
71b2a9e
44a0e97
211d063
5feb8be
c0e8efb
7d468f5
fd8fd1d
766e4c0
d159cbc
461ac3a
535aaaa
28ab29e
78cf54b
e7f2332
125f286
c534e01
a52d4ae
caa0ac4
2d64cc2
a8d2c49
49fb7e9
6d054a3
d8a105f
85dbac7
42ccdf5
d6c6d8f
5d30e22
b452159
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,141 @@ | ||
| ``html_attr_merge`` | ||
| =================== | ||
|
|
||
| .. _html_attr_merge: | ||
|
|
||
| .. versionadded:: 3.23 | ||
|
|
||
| The ``html_attr_merge`` filter was added in Twig 3.23. | ||
|
|
||
| The ``html_attr_merge`` filter merges multiple mappings that represent | ||
| HTML attribute values. Such mappings contain the names of the HTML attributes | ||
| as keys, and the corresponding values represent the attributes' values. | ||
|
|
||
| It is primarily designed for working with arrays that are passed to the | ||
| :ref:`html_attr` function. It closely resembles the :doc:`merge <../filters/merge>` | ||
| filter, but has different merge behavior for values that are iterables | ||
| themselves, as it will merge such values in turn. | ||
|
|
||
| The filter returns a new merged array: | ||
|
|
||
| .. code-block:: twig | ||
|
|
||
| {% set base = {class: ['btn'], type: 'button'} %} | ||
| {% set variant = {class: ['btn-primary'], disabled: true} %} | ||
|
|
||
| {% set merged = base|html_attr_merge(variant) %} | ||
|
|
||
| {# merged is now: { | ||
| class: ['btn', 'btn-primary'], | ||
| type: 'button', | ||
| disabled: true | ||
| } #} | ||
|
|
||
| The filter accepts multiple arrays as arguments and merges them from left to right: | ||
|
|
||
| .. code-block:: twig | ||
|
|
||
| {% set merged = base|html_attr_merge(variant1, variant2, variant3) %} | ||
|
|
||
| A common use case is to build attribute mappings conditionally by merging multiple | ||
| parts based on conditions. To make this conditional merging more convenient, filter | ||
| arguments that are ``false``, ``null`` or empty arrays are ignored: | ||
|
|
||
| .. code-block:: twig | ||
|
|
||
| {% set button_attrs = { | ||
| type: 'button', | ||
| class: ['btn'] | ||
| }|html_attr_merge( | ||
| variant == 'primary' ? { class: ['btn-primary'] }, | ||
| variant == 'secondary' ? { class: ['btn-secondary'] }, | ||
| size == 'large' ? { class: ['btn-lg'] }, | ||
| size == 'small' ? { class: ['btn-sm'] }, | ||
| disabled ? { disabled: true, class: ['btn-disabled'] }, | ||
| loading ? { 'aria-busy': 'true', class: ['btn-loading'] }, | ||
| ) %} | ||
|
|
||
| {# Example with variant='primary', size='large', disabled=false, loading=true: | ||
|
|
||
| The false values (secondary variant, small size, disabled state) are ignored. | ||
|
|
||
| button_attrs is: | ||
| { | ||
| type: 'button', | ||
| class: ['btn', 'btn-primary', 'btn-lg', 'btn-loading'], | ||
| 'aria-busy': 'true' | ||
| } | ||
| #} | ||
|
|
||
| Merging Rules | ||
mpdude marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| ------------- | ||
|
|
||
| The filter follows these rules when merging attribute values: | ||
|
|
||
| **Scalar values**: Later values override earlier ones. | ||
|
|
||
| .. code-block:: twig | ||
|
|
||
| {% set result = {id: 'old'}|html_attr_merge({id: 'new'}) %} | ||
| {# result: {id: 'new'} #} | ||
|
|
||
| **Array values**: Arrays are merged like in PHP's ``array_merge`` function - numeric keys are | ||
| appended, non-numeric keys replace. | ||
mpdude marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| .. code-block:: twig | ||
|
|
||
| {# Numeric keys (appended): #} | ||
| {% set result = {class: ['btn']}|html_attr_merge({class: ['btn-primary']}) %} | ||
| {# result: {class: ['btn', 'btn-primary']} #} | ||
|
|
||
| {# Non-numeric keys (replaced): #} | ||
| {% set result = {class: {base: 'btn', size: 'small'}}|html_attr_merge({class: {variant: 'primary', size: 'large'}}) %} | ||
| {# result: {class: {base: 'btn', size: 'large', variant: 'primary'}} #} | ||
|
|
||
| .. note:: | ||
|
|
||
| Remember, attribute mappings passed to or returned from this filter are regular | ||
| Twig mappings after all. If you want to completely replace an attribute value | ||
| that is an iterable with another value, you can use the :doc:`merge <../filters/merge>` | ||
| filter to do that. | ||
|
|
||
| **``MergeableInterface`` implementations**: For advanced use cases, attribute values can be objects | ||
| that implement the ``MergeableInterface``. These objects can define their own, custom merge | ||
| behavior that takes precedence over the default rules. See the docblocks in that interface | ||
| for details. | ||
|
|
||
| .. note:: | ||
|
|
||
| The ``html_attr_merge`` filter is part of the ``HtmlExtension`` which is not | ||
| installed by default. Install it first: | ||
|
|
||
| .. code-block:: bash | ||
|
|
||
| $ composer require twig/html-extra | ||
|
|
||
| Then, on Symfony projects, install the ``twig/extra-bundle``: | ||
|
|
||
| .. code-block:: bash | ||
|
|
||
| $ composer require twig/extra-bundle | ||
|
|
||
| Otherwise, add the extension explicitly on the Twig environment:: | ||
|
|
||
| use Twig\Extra\Html\HtmlExtension; | ||
|
|
||
| $twig = new \Twig\Environment(...); | ||
| $twig->addExtension(new HtmlExtension()); | ||
|
|
||
| Arguments | ||
| --------- | ||
|
|
||
| The filter accepts a variadic list of arguments to merge. Each argument can be: | ||
|
|
||
| * A map of attributes | ||
| * ``false`` or ``null`` (ignored, useful for conditional merging) | ||
| * An empty string ``''`` (ignored, to support implicit else in ternary operators) | ||
|
|
||
| .. seealso:: | ||
|
|
||
| :ref:`html_attr`, | ||
| :doc:`html_attr_type` | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,123 @@ | ||
| ``html_attr_type`` | ||
| ================== | ||
|
|
||
| .. _html_attr_type: | ||
|
|
||
| .. versionadded:: 3.23 | ||
|
|
||
| The ``html_attr_type`` filter was added in Twig 3.23. | ||
|
|
||
| The ``html_attr_type`` filter converts arrays into specialized attribute value | ||
| objects that implement custom rendering logic. It is designed for use | ||
| with the :ref:`html_attr` function for attributes where | ||
| the attribute value follows special formatting rules. | ||
|
|
||
| .. code-block:: html+twig | ||
|
|
||
| <img {{ html_attr({ | ||
| srcset: ['small.jpg 480w', 'large.jpg 1200w']|html_attr_type('cst') | ||
| }) }}> | ||
|
|
||
| {# Output: <img srcset="small.jpg 480w, large.jpg 1200w"> #} | ||
|
|
||
| Available Types | ||
| --------------- | ||
|
|
||
| Space-Separated Token List (``sst``) | ||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
|
|
||
| Used for attributes that expect space-separated values, like ``class`` or | ||
| ``aria-labelledby``: | ||
|
|
||
| .. code-block:: html+twig | ||
|
|
||
| {% set classes = ['btn', 'btn-primary']|html_attr_type('sst') %} | ||
|
|
||
| <button {{ html_attr({class: classes}) }}> | ||
| Click me | ||
| </button> | ||
|
|
||
| {# Output: <button class="btn btn-primary">Click me</button> #} | ||
|
|
||
| This is the default type used when the :ref:`html_attr` function encounters an | ||
| array value (except for ``style`` attributes). | ||
|
|
||
| Comma-Separated Token List (``cst``) | ||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
|
|
||
| Used for attributes that expect comma-separated values, like ``srcset`` or | ||
| ``sizes``: | ||
|
|
||
| .. code-block:: html+twig | ||
|
|
||
| <img {{ html_attr({ | ||
| srcset: ['image-1x.jpg 1x', 'image-2x.jpg 2x', 'image-3x.jpg 3x']|html_attr_type('cst'), | ||
| sizes: ['(max-width: 600px) 100vw', '50vw']|html_attr_type('cst') | ||
| }) }}> | ||
|
|
||
| {# Output: <img srcset="image-1x.jpg 1x, image-2x.jpg 2x, image-3x.jpg 3x" sizes="(max-width: 600px) 100vw, 50vw"> #} | ||
|
|
||
| Inline Style (``style``) | ||
| ~~~~~~~~~~~~~~~~~~~~~~~~ | ||
|
|
||
| Used for style attributes. Handles both maps (property - value pairs) and sequences (CSS declarations): | ||
|
|
||
| .. code-block:: html+twig | ||
|
|
||
| {# Associative array #} | ||
| {% set styles = {color: 'red', 'font-size': '14px'}|html_attr_type('style') %} | ||
|
|
||
| <div {{ html_attr({style: styles}) }}> | ||
| Styled content | ||
| </div> | ||
|
|
||
| {# Output: <div style="color: red; font-size: 14px;">Styled content</div> #} | ||
|
|
||
| {# Numeric array #} | ||
| {% set styles = ['color: red', 'font-size: 14px']|html_attr_type('style') %} | ||
|
|
||
| <div {{ html_attr({style: styles}) }}> | ||
| Styled content | ||
| </div> | ||
|
|
||
| {# Output: <div style="color: red; font-size: 14px;">Styled content</div> #} | ||
|
|
||
| The ``style`` type is automatically applied by the :ref:`html_attr` function when | ||
| it encounters an array value for the ``style`` attribute. | ||
|
Comment on lines
+85
to
+86
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This would have been the case for both previous example, no? I mean, what would happen to both examples without the
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, it would, because of the default handling of attributes named TBH I am not sure if this type should be exposed in the first place. WDYT – do you see any situation where one might want to have inline CSS in another attribute? That could be used to improve this section. |
||
|
|
||
| .. note:: | ||
|
|
||
| The ``html_attr_type`` filter is part of the ``HtmlExtension`` which is not | ||
| installed by default. Install it first: | ||
|
|
||
| .. code-block:: bash | ||
|
|
||
| $ composer require twig/html-extra | ||
|
|
||
| Then, on Symfony projects, install the ``twig/extra-bundle``: | ||
|
|
||
| .. code-block:: bash | ||
|
|
||
| $ composer require twig/extra-bundle | ||
|
|
||
| Otherwise, add the extension explicitly on the Twig environment:: | ||
|
|
||
| use Twig\Extra\Html\HtmlExtension; | ||
|
|
||
| $twig = new \Twig\Environment(...); | ||
| $twig->addExtension(new HtmlExtension()); | ||
|
|
||
| Arguments | ||
| --------- | ||
|
|
||
| * ``value``: The sequence of attributes to convert | ||
| * ``type``: The attribute type. One of: | ||
|
|
||
| * ``sst`` (default): Space-separated token list | ||
| * ``cst``: Comma-separated token list | ||
| * ``style``: Inline CSS styles | ||
|
|
||
| .. seealso:: | ||
|
|
||
| :ref:`html_attr`, | ||
| :ref:`html_attr_merge` | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The documentation for those functions and filters should include the note about the fact that they are part of an extra extension, not of Twig core (see the documentation of the
html_classesfunction or thedata_urifilter for existing usages)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, I see that it is actually present inside the section about merging rules. This is confusing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's always at the end of the documentation pages, like for example with
html_cva– those other pages also have other preceding sub-headlines.