symfony / ux

Symfony UX initiative: a JavaScript ecosystem for Symfony

Home Page:https://ux.symfony.com/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

AlpineJS specific attributes are not rendered (`@click` and `x-on:click`)

loevgaard opened this issue · comments

I love the components, so thank you <3

I have a button component and I would like to be able to do:

<twig:Button value="Toggle" x-on:click="open = !open"/>

but the x-on:click attribute is not rendered with this Twig:

<button{{ attributes }}>{{ value }}</button>

instead it just strips the attribute and outputs:

<button>Toggle</button>

If I try to use the @click syntax, Twig throws an exception.

I think this collides with newly implemented nested attributes.
In your case it would be accessible with attributes.nested('x-on') which in your case is unexpected :)

Ah yes, I think @norkunas is correct. Anyone have any thoughts on how to solve?

If I try to use the @click syntax, Twig throws an exception.

I think this could be a html-twig component parsing issue.

What does the expection say ? In the end i'm not surprised as it's not valid HTML syntax. But if we can improve this let's do it :)

I think this collides with newly implemented nested attributes.
In your case it would be accessible with attributes.nested('x-on') which in your case is unexpected :)

Now that you pointed me in the right direction, it's totally fine for me :)

I will let you guys close the issue if you believe this is not something you want to 'fix'. For me, when I know what I know now, it's perfect :)

Once again, thanks for a great component!

I think this is legit issue we should keep it open. I don't really know how to solve it... Should we detect alpine attribute? Or should we add a prefix so that the attributes are ignore by the nested? Should we add a prefix for every nested attribute like <twig:Form twig:row:label='password' />?

I don't know if it's possible, but there's also the option to use another separator for nested attributes, e.g. dot:

<twig:Button value="Toggle" nested.attribute="Value"/>

But then again, that might conflict with something else...

But then again, that might conflict with something else...

Yes this is my worries!

Yeah, no matter what syntax we use, the possibility for conflicts exists. Some quick ideas:

  1. Make the nested separator configurable
  2. Add a prefix exclude-list
  3. Some kind of escape syntax (ie !x-on:click)

1 & 2 above don't address the issue of using components from 3rd party bundles that would use the default syntax (and be unaware of your configuration). 3 feels like the best but introducing a new syntax isn't ideal.

Now that you pointed me in the right direction, it's totally fine for me :)

I'm glad there's a workaround but still, it's a bit of a bummer. I agree, let's keep this open.

The practical approach would be to do the research of the top 10-20 JS libraries that use this kind of syntax and see how they do it and make an informed decision based on that. Then at least it would hurt the least amount of people. Just thinking out loud :)

This can be a good solution, but some librairies can have conflic, and all of this convention could lead to limitation to the user. So I am not sure about this solution as well

Do we accept "dashes" in the component names ? Maybe we could say if "x-...:" it is not a nested param ?

With the knowledge of nested attributes I just tried this:

<twig:Button x-on:click="hidden = false" value="Test"/>

and this

<button{{ attributes.nested('x-on') }}>{{ value }}</button>

and it renders as

<button click="hidden = false">Test</button>

which made me read the docs, and of course it makes sense, but now the user experience of adding these AlpineJS directives just got a bit worse in my opinion. Now I have to prepend x-on: to every value in the attributes.nested('x-on') collection.

Here's my code to do that:

{% set xOnAttributes = attributes.nested('x-on') %}
{% set xOn = '' %}
{% for attr, value in xOnAttributes %}
    {% set xOn = ' x-on:' ~ attr ~ '="' ~ xOnAttributes.render(attr) ~ '"' ~ xOn %}
{% endfor %}

This is pretty ugly :D

Ended up making a twig filter like this:

<?php

declare(strict_types=1);

namespace App\Twig\Extension;

use Symfony\UX\TwigComponent\ComponentAttributes;
use Twig\Extension\AbstractExtension;
use Twig\TwigFilter;
use Twig\TwigFunction;

final class Extension extends AbstractExtension
{
    /**
     * @return list<TwigFilter>
     */
    public function getFilters(): array
    {
        return [
            new TwigFilter('nested_attributes', $this->nestedAttributes(...), ['is_safe' => ['html']]),
        ];
    }

    public function nestedAttributes(ComponentAttributes $attributes, string $namespace): string
    {
        $attributes = $attributes->nested($namespace);

        $output = '';

        foreach ($attributes as $attr => $_) {
            $output .= sprintf(' %s:%s="%s"', $namespace, $attr, $attributes->render($attr));
        }

        return $output;
    }
}

then I can output the x-on nested attributes like this:

<button{{ attributes|nested_attributes('x-on') }}>{{ value }}</button>