-
-
Notifications
You must be signed in to change notification settings - Fork 364
[Twig] Add attribute merging behaviour #1404
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
Conversation
cd76136
to
6a59ca7
Compare
This one make me a bit anxious... as it's kinda a BC break... but i know how many of you want this feature out :) i'm wondering if we should have the "mode" supported by the attribute key and not the value.
Does this work only on declared attributes ? Or all ? In wich case this could create strange behaviour with mount / events no ? :| |
How so? Only thing I could think of is someone using this type of notation already for something else so I tried to choose something very obscure.
Do you mean something like? <twig:Component class@="replace" class^="prefix" class$="suffix" /> Quick test shows that some changes would be needed in the
I could be missing something but I can't see how it could affect anything else - this is all internal to |
I mean there is already a lot of different things and usage for those values... When you type class="$:..." you have no idea if it's a mounted property, an attribute, etc etc. So i'm a bit mixed for now, as I really feel there are great features coming (and it's super cool)... but it may happen people don't have in mind all the interactions between twig component / live component / .... and all the ways data is handled / displayed / saved. So not especially for this one, more a general feeling we should isolate things a bit. -- Concerning those evolutions i still believe i'll want to protect some PHP attribute / use some readonly, but that absolutely can be done later (and by me if i'm the one asking for it haha) -- So let's focus on your PR for now :) |
<twig:Component class@="replace" class^="prefix" class$="suffix" /> I think this is a no-go, even though I did like the idea of I think, whatever the solution, it is going to need to live inside the component itself so that the usage can remain as normal HTML. |
Agree that adjusting the html syntax would be difficult and wouldn't work with non-html syntax components.
|
6a59ca7
to
2eedef9
Compare
2eedef9
to
8ed96c3
Compare
Note, in its current form, only when rendering the component can you use this notation. You cannot use it in |
What about this syntax? <div
class=“{{ attributes.class }} foo bar”
should-override=“{{ attributes.get(‘should-override)|default(‘default-val’) }}
{{ attributes }}
> It would work like the form system: as you reference attributes like “class”, they’re marked as rendered. At the end, it renders anything remaining. This gives users full control over merge behavior, you could use filters like tailwind_merge and it allows you to write native HTML attributes. The only gotcha would be to know not to write class”foo” without remembering to also render |
But only from inside the component, correct? You couldn't choose the merge behavior when rendering a component (maybe this isn't a realistic scenario as someone pointed out in the livestream chat).
In theory, I like this. It would require |
I'm 100% for something decided on the "render" side and not the "calling" side. |
Ok, that gives us more options for sure - something like: {# MyComponent #}
<div{{ attributes
.defaultPrepend('data-controller', 'default')
.defaultAppend('style', 'default')
.defaultReplace('class', 'default')
}} />
{# render #}
<twig:MyComponent data-controller="foo" style="bar" class="baz" />
{# html #}
<div data-controller="default foo" style="bar default" class="baz" /> |
Do you like that version better than my “pure attributes” version? I’m kinda solving the prepend problem AND the syntax in one go… if we like that one. it also allows for things like tailwind merge. It feels a bit better to just be echoing things vs configuring and object perfectly so that once it is printed in its entirety, it prints correctly. But I know we borrowed the current implementation from blade, so it’s possible my way is not considering some case. |
Not necessarily better, I was really just pointing out that only allowing controlling the behaviour in the component allows us to remove the weird syntax I came up with.
You're right, it makes that easier. Is this what you have in mind? <div
class="{{ (attributes.class ~ " foo bar")|tailwind_merge }}"
> That's a bit verbose but I'm sure it could be improved/simplified. Maybe: <div
class="{{ attributes.class.append('foo bar')|tailwind_merge }}"
class="{{ attributes.class.prepend('foo bar')|tailwind_merge }}"
class="{{ attributes.class.default('foo bar')|tailwind_merge }}"
> |
Yup! That’s what I had in mind :). Though running an attribute where you need to merge with defaults and use a filter is an edge case (and I still think we could tailwind merge on class automatically). So I wouldn’t optimize for that case. But if these .prepend style functions are optional (meaning, you can just do |
Yep, calling get() would return a stringable object
You're ok with breaking Edit: actually, could that be a problem for live components? If we unset the values, they won't be available on re-render? I think it would be but I'll have to confirm |
Just saw the live stream of last week and I like this approach, however I wanted to slide in here and ask a question: <{{ attributes.element|default('div') }}
class=“{{ attributes.class }} foo bar”
should-override=“{{ attributes.get(‘should-override)|default(‘default-val’) }}
{{ attributes }}
>
<twig:SomeOtherComponent an-other-attribute="{{ attributes.something }}" />
</{{ attributes.element|default('div') }} So in this case, So maybe this is not the correct approach what I am trying here, but wouldn't it be benefitial to have also an option to not render specific attributes and leave them for later usage, even if |
@CMH-Benny rendering flag aside, is #1405 similar to what you are thinking? |
@kbond Interesting PR for sure, but it's about passing down attributes of a component down to a specific block, which is also interesting to have and a great addition for sure! I am very interested in symfony-ux lately and try to theory-craft how I could use it in our project and come up with different ideas that raise such kind of questions, so they mostly are theoretical as of now, but before I start to actually implement that, I want to be sure I know what I can do and if I can do what I need. So this passing down of attributes that are put on a component down to another element was one of those questions and i think it fits here quite well, because append,prepend,merge,replace and preserve is kinda another option for attributes. I am coming from a different thought, I wondered if it would be possible to create some Interfaces for my Components like "Collabsible" and have a base component template that checks for this interface and in that case adds additional attributes in twig. So every component that implements this Interface and uses the base template as a start point would get this "for free" - But I am not sure if extending/inheriting a twig component template form a base one is even possible, especially because of that attributes handling we have. Don't want to hijack this PR for that, but also not sure where to put that question and since it's related, maybe you can point me to the correct place? Or tell me that I am too crazy 😂 |
Closing in favour of #1442 |
This PR was merged into the 2.x branch. Discussion ---------- [Twig] Add (alternate) attribute rendering system | Q | A | ------------- | --- | Bug fix? | no | New feature? | yes | Issues | n/a | License | MIT This is an alternate to #1404 as suggested by Ryan [here](#1404 (comment)). This PR allows the following: ```twig <div class="{{ attributes.render('class') }} appended-default" style="prepended-default {{ attributes.render('style') }}" data-foo="{{ attributes.render('data-foo')|default('replaced-default') }}" {{ attributes }} > ``` When calling `attributes.render('name')` ~(or magically via `attributes.name`)~, the attribute is marked as _rendered_. Later when calling just `{{ attributes }}`, the attributes marked as already rendered are excluded. Whether or not an attribute is considered rendered is only evaluated when converting `ComponentAttributes` to a string. TODO: - [x] Docs - [x] Add test ensuring works in real twig component Commits ------- 0d027d5 feat(twig): Add attribute rendering system
Currently, when using attribute defaults, the merging behaviour of individual attribute values is a bit arbitrary,
class
,data-controller
anddata-action
keep the default value as the prefix but any other completely overrides the default:This PR proposes an attribute value prefix notation to configure the behaviour:
^:
- prefix$:
- suffix@:
- replace (default except for the exceptions above - this allows configuring these exceptions)Suggestions on a better/alternate notation or even an alternate way to acheive is welcome!
Usage:
I wish we could remove the special exceptions but not sure the best way to do this. We could deprecate not passing a behaviour for these. And set their default to "replace" in 3.0 but this might be heavy handed.