-
-
Notifications
You must be signed in to change notification settings - Fork 356
[Twig] ComponentAttributes::append()/prepend()
#1531
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
When working on this I kept getting confused by append/prepend (what's prepended/appended, the real value or the default?). I think in most cases the order here doesn't matter, or am I wrong? Could we reduce to a single method that just joins a default value with the actual value? If so, what should this method be called? |
01f565f
to
21dae51
Compare
prefix ? |
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.
Haha thanks @kbond! I had the same idea while working on my PR! You are too fast! 😁
The order does indeed matter when using |
So |
I know this already merged but what do you think about the following syntax: <div
class="{{ attributes.render('class', 'default') }}" {# second parameter is the default value #}
style="{{ attributes.render.before('display:block;') }}" {# second parameter value to append #}
data-controller="{{ attributes.render.after('hello-controller') }}" {# second parameter value to prepend #}
{{ attributes }} {# be sure to always render the remaining attributes! #}
>
My Component!
</div> |
Yes, that seems more explicit - maybe
I originally had render return an object where we could chain these types of methods. I think it reads nicer too. We ended up dropping as it removed the possibility to do:
Since render would always return the object Seeing this is a super new feature, we could probably get away with sneaking in this obscure BC break? |
I am voting to sneak a bit here 😈 |
Me to, @weaverryan, @smnandre, any objections? I'm thinking the following: {# templates/components/MyComponent.html.twig #}
<div
class="{{ attributes.render('class').default('default') }}"
style="{{ attributes.render('style').before('display:block;') }}"
data-controller="{{ attributes.render('data-controller').after('hello-controller') }}"
{{ attributes }}
>
My Component!
</div> |
? |
I'd feel easier to get something like renderAfter / renderBefore but as you want |
This would no longer work (and be the BC break here). render() would always return the new object.
I don't have a strong opinion but yes, renderAfter/renderBefore would be a bit simpler and not create a BC break. |
Hmm... if you make this object countable that'd be helpful :) |
What do you think if the function renders instead of returning null return a new object called // this work since ComponentAttribute is Stringable
class="{{ attributes.render('class') ?? 'default' }}"
// but also all of this
<div
class="{{ attributes.render('class').default('default') }}"
style="{{ attributes.render('style').before('display:block;') }}"
data-controller="{{ attributes.render('data-controller').after('hello-controller') }}"
{{ attributes }}
>
My Component!
</div> so the function default, before, and after will be on the ComponentAttribute object |
Wouldn't it need to be changed to the following? - {{ attributes.render('class') ?? 'default' }}
+ {{ attributes.render('class') ?: 'default' }} Also, and we'd need to double check, but at the time |
Ha yes sorry, so the function render returns null or the new ComponentAttribute object. |
If you implement countable the ?: would work |
I don't want it to return null as that would require an extra check when chaining .before()/after().
Why would it work for countable but not a stringable object that returns an empty string? I'm not a huge fan of making it countable just for this purpose. |
Oh it was just to give a fun fact :)) I'm 100% ok with your proposal :) Count is called before empty, i discoverd that after a loooong afternoon of debugging (when Jane Automapper started to use ArrayObject with no public properties in a minor version 😅.... making every |default working in the opposite way than excpected :p ) |
After a while i'm starting to thing.... why passing to a method something that can be direct in the HTML ? <div style="{{ attributes.render('style').before('display:block;') }}" > is much easier to read as <div style="display:block; {{ attributes.render('style') }}"> Same for "after"... no ? |
And what could improve DX would be magic getter i think <div style="display:block; {{ attributes.style }}" class="{{ attributes.class }}"> |
Totally valid and maybe preferred by some. The primary reason I added was to make it prettier when using something like tailwind_merge.
One problem with using the magic methods to avoid calling ->render() is there's no longer an error for a bad method call (ie a typo in defaults()): {{ attributes.dfaults({class: 'block'}) }} === null |
Ok lets Forget then :) |
Hi! A) The 2nd arg of B) The prepend/append idea seems to be entirely wanted / needed JUST for class="{{ (attributes.render('class')~' bg-red-500')|tailwind_merge }}" That is a bit ugly :). Could class="{{ tailwind_merge(attributes.render('class'), 'bg-red-500') }}" Or this? class="{{ [attributes.render('class'), 'bg-red-500']|tailwind_merge }}"
|
I agree, I like the idea of unlimited arguments in both cva and tailwind_merge. It really seems like the most explicit way to prepend/append when compared to:
|
Closing as there are, I think, better solutions. |
This is a followup to #1442.
When reviewing #1416, I noticed how using
~
to build attribute values with justrender()
was a bit annoying. This PR adds the following:render()
(render('class', 'default')
) to avoid needing to userender('class') ?? 'default'
prepend()
/append()
to avoid needing to to useclass="{{ attributes.render('class') }} some defaults"
Example: