-
Notifications
You must be signed in to change notification settings - Fork 11.1k
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
[8.x] Blade component slot attributes #38372
[8.x] Blade component slot attributes #38372
Conversation
That would be awesome if merged. Currently I use some custom props like |
While I appreciate this feature, I want to really make sure it's needed. In both of your examples could you not have just done this type of thing: <x-slot name="heading">
<span class="font-bold">Heading</span>
</x-slot> Does Vue support this? |
@taylorotwell Remember when we talked about scoped slots for Blade? In the implementation I used (https://twitter.com/samuelstancl/status/1390655396927447043), I had no way to access other attributes on the Dan's feature would let me use: <x-slot name="foo" scoped>
</x-slot> I'm actually happy to contribute the scoped slots feature if Dan's PR is merged. The code is super simple and the only ugly bits currently are the ones that check the |
Thanks for the consideration! This is definitely needed. The example I gave was purely of the implementation really, I agree that it's not a good example to demonstrate its power. Where this really comes in useful is outside of simple class territory. For example - if the wrapper around the slot is not a simple <textarea {{ $textarea->attributes }}>
{{ $textarea }}
</textarea> And - Vue does not support this yet, but I saw someone mention earlier that they'd love it if it was 😅 |
As per need it's good to have when you extract the components in a package with most of the functionality implemented in component and you might want to avoid extra markup. @php
$activatorTag = $activtor->attributes->get('tag', 'div');
@endphp
<{{ $actrivatorTag }}
{{ $activator->attributes->except(['tag'])->class(['transition-all']) }}
x-ref="activator"
x-on:click="openOnClick && !isActive ? show() : hide()"
x-on:focus.debounce.150ms="openOnFocus && show()">
{{ $activator }}
</{{ $actrivatorTag }}> <x-dropdown>
<x-slot name="activator" class="..." tag="button" disabled>
Settings
<x-slot>
...
</x-dropdown> |
@danharrin I see that implementation is almost same as we are using. We faced an edge case when using this approach in
protected function compileSlots(array $slots)
{
return collect($slots)->map(function ($slot, $name) {
return $name === '__default' ? null : '<x-slot name="'.$name.'">{{ $'.$name.' }}</x-slot>';
})->filter()->implode(PHP_EOL);
} I solved this overriding dynamic component like so protected function compileSlots(array $slots)
{
return collect($slots)->map(function ($slot, $name) {
$slot->attributes = ($slot->attributes instanceof ComponentAttributeBag) ? $slot->attributes : new ComponentAttributeBag();
$slot->attributes->setAttributes($slot->attributes->getAttributes());
return $name === '__default' ? null : '<x-slot name="' . $name . '" ' . $slot->attributes->__toString() . '>{{ $' . $name . ' }}</x-slot>';
})->filter()->implode(PHP_EOL);
} |
@danharrin any thoughts on the dynamic component note above? |
@taylorotwell I'll add a test for dynamic components to check it's an actual issue, because looking at @binaryweavers code, I can't see why that would do anything but replace the already existing slot attribute bag 😅 it should be created when the slot is constructed, so should always be present. |
Haha TBH, I am not sure why I did that, I was lacking time and was facing an issue as well (you see I don't have tests as well 🤦♂️). |
Looks like this works fine if you cast the attributes to a string and pass them through normally 👍 No need to reset the attribute bag. |
Hahah don't worry I got you covered and backtraced that confusing part of mine code 😂 I see that you missed that too. Here is the breakdown first.
$slot->attributes were not working as it gets collected in componentData method.then I blindly patched those confusing lines in DynamicComponent 🤦♂️Then later on I updated here . So Those two line are just doing nothing there now. |
@binaryweavers @taylorotwell I think I've fixed it, but have a check yourselves if you wish! |
Yes they are unnecessary after making default |
@danharrin tests are failing |
@taylorotwell I tested your example and, as far as I can see, escaping bound attributes has no effect on slot attribute output or component attribute output. Am I missing something? |
Clear your compiled views. It does escape for me when I switch to escapeBound = true on slot data. |
Yeah I had cleared them and the issue was there to start with. It's all good as long as you're happy with it working as you expect :) |
Hmm - there is still the XSS vulnerability in the PR? Any user provided data that is bound such as alt text, etc. will be vulnerable to XSS. |
Ah, nevermind, I see you started escaping them now. |
* Update slot pattern * Add attributes params to existing tests * Create ComponentSlot class * Pass attributes from slot tag to Blade directive * Compile slot attributes into slot object * Add compilation tests for attribute support * Remove unused exception * Reorder arguments * Fix dynamic components with slot attributes * Escape bound attributes for slots * Update BladeComponentTagCompilerTest.php * formattinG Co-authored-by: Taylor Otwell <taylorotwell@gmail.com>
Nice feature that I missed time ago. Thanks. Seem not possible to pass to a component the slot attributes, like: <div {{ $attributes) }}>
<x-mycomponent {{ $myslot->attributes->class('my-class') }}>{{ $myslot }}</x-mycomponent>
{{ $slot }}
</div> |
This doesn't work: <x-my-component {{ $myslot->attributes }}>{{ $myslot }}</x-my-component> While this works: <x-my-component :attributes="$myslot->attributes">{{ $myslot }}</x-my-component> But with main $attributes this works: <x-my-component {{ $attributes }}>{{ $slot }}</x-my-component> @danharrin is this expected behaviour? The error message is: "syntax error, unexpected token "else", expecting end of file " |
@thewebartisan7 This components rendering with arbitrary blade-injected content is not possible at the moment. You can't use <x-my-component @if ($condition) class="text-red-500" @endif>
{{ $slot }}}}
</x-my-component> Your second example works because you bind |
That make sense. Thanks. |
Good day and thank you for this invaluable and educative post. May I ask if it is possible to check if a named slot has data/content before rendering its content? In other words, how do I make named slots optional? An if statement using the named slot throws an error, your guidance would be highly appreciated.
However, when you check for emptiness on the main slot, it works.
Without the ability to make named slots optional, one is now compelled to litter code with empty named slots just to avoid errors
|
If your slot is nullable, you should delare it in Then you can just use |
Thank you very much for guiding me into resolving the error I had. |
This PR further enhances the power of Blade components by allowing slots to have their own attributes. 🚀
Take this
card
component example:In this example, I am able to fully customise multiple areas of the Blade component, not just its outer container. By passing the
class
attribute to both slots, I can make the heading bold and the footer smaller without having to create another view just for this use case.Let me know if you have any questions about this implementation, or you can see any areas that could be improved!