-
-
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
b3bf116
d0b46d7
987fbd2
0b14080
10ce9c6
5ca5ac1
04e8549
9b1cb14
5be60b3
65651a8
53abfba
9c21487
1786118
bad3a71
6b4a02b
29a7032
dc01c02
a453ca2
fefbaf2
5e7bf9c
38c7d3e
8ec8bfb
f6ff09d
56db058
06c96c8
830be2c
6254232
bf33bdf
62b2584
ca4ad98
2668577
3aee494
0f82b04
b3d62dc
fbb0bb5
a80bdb4
bd41ee6
e8c36bf
230bcf7
352e564
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.