Skip to content

Commit c8e7578

Browse files
committed
Added ability to add named slots to x-livewire components
1 parent 3090fb8 commit c8e7578

File tree

4 files changed

+144
-14
lines changed

4 files changed

+144
-14
lines changed

README.md

Lines changed: 52 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,17 +51,66 @@ ie: `class Alert extends XLivewireBaseComponent{`
5151
My alert message
5252
</x-livewire>
5353
```
54-
5. You can access the `$slot` and `$attributes` variables just like you would in a Blade component:
54+
-------------------------------------------------------------------------------------
55+
You can access the `$slot` and `$attributes` variables just like you would in a Blade component:
5556
```
5657
{{ $slot }}
5758
{{ $attributes->get('title') }}
5859
```
59-
6. You can also access the array of attributes that were passed to the x-livewire's component's tag but were not explicitedly declared in the class as
60+
61+
62+
You can also access the array of attributes that were passed to the x-livewire's component's tag but were not explicitedly declared in the class as
6063
`$tagAttributes` property.
6164
```
6265
{{ $tagAttributes->get('href') }}
6366
```
64-
For example, attributes like `primary`, `lg` etc that don't need corresponding properties in the class..
67+
For example, attributes like `primary`, `lg` etc that don't need corresponding properties declarations in the class.
68+
E.g
69+
```HTML
70+
<x-livewire _="link" href="https://google.com" primary>Google </x-livewire>
71+
....
72+
73+
<span>
74+
<a href="{{ $tagAttributes->get('href') }}>{{ $slot }}</a>
75+
</span>
76+
```
77+
78+
79+
You can add and access named slots as such:
80+
``` <x-livewire _="alert" title="Warning">
81+
My alert message
82+
<x-slot name="footer">My custom footer </x-slot>
83+
</x-livewire>
84+
85+
....
86+
87+
<div class="alert ...">
88+
{{ $slot }}
89+
<div class="alert-footer">
90+
{{ $footer ?? 'Default footer content' }}
91+
</div>
92+
</div>
93+
```
94+
95+
If you want to access the slots directly as their ` Illuminate\View\ComponentSlot ` class, you can use the following:
96+
`$this->laravelSlots()['footer']`.
97+
Which would return an instance of `Illuminate\View\ComponentSlot`.
98+
E.g:
99+
``` "footer" => Illuminate\View\ComponentSlot {#1385 ▼
100+
+attributes: Illuminate\View\ComponentAttributeBag {#1379 ▼
101+
#attributes: []
102+
}
103+
#contents: "<b>&lt;i&gt;hello!!! &lt;/i&gt; </b>"
104+
```
105+
With available methods such as
106+
107+
```public __construct($contents = '', $attributes = array()): void Create a new slot instance.
108+
public withAttributes(array $attributes): $this Set the extra attributes that the slot should make available.
109+
public toHtml(): string Get the slot's HTML string.
110+
public isEmpty(): bool Determine if the slot is empty.
111+
public isNotEmpty(): bool Determine if the slot is not empty.
112+
public __toString(): string Get the slot's HTML string.
113+
```
65114
## Testing
66115

67116
```bash
@@ -77,8 +126,6 @@ Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed re
77126

78127
[ ] Shorten tag declartion to `<x-livewire:alert>`
79128

80-
[ ] Allow custom slots
81-
82129
## Contributing
83130

84131
Please see [CONTRIBUTING](https://github.com/titonova/.github/blob/main/CONTRIBUTING.md) for details.

resources/views/components/livewire.blade.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
<livewire:is
33
:component="$attributes->get('_')"
44
:slot="serialize($slot)"
5+
:laravel-slots="serialize($__laravel_slots)"
56
:attributes="serialize($attributes)"
67
/>
8+
9+
710
</span>

src/Components/XLivewireBaseComponent.php

Lines changed: 52 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,18 @@
88
class XLivewireBaseComponent extends Component
99
{
1010

11-
public $slot;
12-
public $attributes;
11+
public string $slot;
12+
public string $attributes;
13+
14+
15+
/**
16+
* The serialized array of custom slots passed to the component.
17+
* Basically, x-livewire's version of the default Blade $__laravel_slot variable.
18+
*
19+
*
20+
* @var string
21+
*/
22+
public string $laravelSlots;
1323

1424
public function slot()
1525
{
@@ -21,12 +31,47 @@ public function attributes()
2131
return unserialize($this->attributes);
2232
}
2333

24-
public function setProps()
34+
public function laravelSlots()
35+
{
36+
return unserialize($this->laravelSlots);
37+
}
38+
39+
40+
public function mount(){
41+
$this->setProps();
42+
}
43+
44+
/**
45+
* Set the component's class properties from the attributes passed into it's x-livewire tag.
46+
*
47+
* What this method does is loop through all it's public properties and set them to the values given in the tag.
48+
* If the property is not public, it is added to the $tagAttributes array.
49+
*
50+
* E.g: Let's say we have an x-livewire tag as follows:
51+
* <x-livewire: _="my-component" :my-attribute="my-value" another-attribute="foofoo" my-tag-attribute="dont-add-me" />
52+
* And its component class is defined as follows:
53+
* class MyComponent extends XLivewireBaseComponent{... public $myAttribute; public $anotherAttribute; ...}
54+
*
55+
* In this case, the component will have the following properties set:
56+
* $this->myAttribute = "my-value"
57+
* $this->anotherAttribute = "foofoo"
58+
*
59+
* There won't be any properties set for the `my-tag-attribute`, as there was no public property with that name($myTagAttribute).
60+
* It would have been added to the $tagAttributes array instead.
61+
* To access it, you would use the following code:
62+
* $this->tagAttributes["my-tag-attribute"]
63+
*
64+
* This method should be the first method called in the component's `mount()` method.
65+
*
66+
* @return void
67+
*/
68+
public function setProps(): void
2569
{
26-
// We name it this way to avoid conflicts with the component's own $attributes property.
70+
// The collection of all attributes that were set in the x-livewire tag.
71+
// We name it this way to avoid conflicts with the component's actual $attributes property.
2772
$this->attributesCollection = collect($this->attributes());
2873

29-
// Get a collection the names of all the public properties.
74+
// Get a collection of the names of all the public properties.
3075
$r_object = new \ReflectionObject($this);
3176
$public_props = collect($r_object->getProperties(\ReflectionProperty::IS_PUBLIC))->transform(fn($prop) => $prop->name);
3277

@@ -47,10 +92,10 @@ public function setProps()
4792
// This is the name of the property component's backend
4893
$prop_livewire_name = Str::snake($prop,'-');
4994

50-
// Check if the property was explicitely set in the $attributes array.
95+
// Check if the property was explicitely set in the $attributes array....
5196
if($this->attributesCollection->has($prop_livewire_name)){
5297

53-
// If it was explicitely set, set the property to the value from the $attributes array.
98+
// ...If the property was explicitely set, set the property to the value from the $attributes array.
5499
$this->$prop = $this->attributesCollection->get($prop_livewire_name);
55100
}
56101
}

src/XLivewireServiceProvider.php

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,17 +35,19 @@ public function bootingPackage()
3535
$attributes = $this->attributes();
3636
3737
/**
38-
* This is9would be) an array of all attributes that were set in the x-livewire tag
38+
* This is would be an array of all attributes that were set in the x-livewire tag
3939
* But were not declared in the component class.
4040
*
41+
*
42+
*
4143
*/
4244
4345
$this->tagAttributes = [];
4446
4547
if($attributes instanceof \Illuminate\View\ComponentAttributeBag){
4648
$slot = $this->slot()->toHtml() ;
4749
48-
/**
50+
/**
4951
* Loop through all the attributes passed in the livewire tag
5052
* and make them variables and class properties to be used in the
5153
* view and livewire component.
@@ -65,6 +67,39 @@ public function bootingPackage()
6567
}
6668
unset($key, $value);
6769
}
70+
/**
71+
* Extract the (named) slot values from the $__laravel_slot variable.
72+
* Thus allowing access to custom, named slots in the view.
73+
*
74+
* That is, Loop through all the slots in the $laraveSlots array and set the property name to the
75+
* camel case version of the slot name and the value to the slot value.
76+
*
77+
* If the slot value is an empty string, it will be set to null so that null checks
78+
*
79+
*
80+
*/
81+
foreach ($this->laravelSlots() as $slot_name => $slot_value) {
82+
if($slot_name !=="__default"){
83+
${Str::camel($slot_name)} = $slot_value->toHtml() == "" ? null : $slot_value->toHtml();
84+
}
85+
86+
87+
}
88+
89+
90+
91+
/**
92+
* Allow $tagAttributes to be accessed from the view.
93+
* e.g {{ $tagAttributes["my-tag-attribute"] }}
94+
*/
95+
96+
$tagAttributes = $this->tagAttributes;
97+
98+
/**
99+
* Clean up unneccessary variables that were set
100+
*/
101+
unset($slot_name, $slot_value, $camelKey);
102+
68103
}
69104
?>';
70105
});

0 commit comments

Comments
 (0)