spatie/laravel-blade-x

Livewire Support

calebporzio opened this issue ยท 14 comments

Consider an app with an input-text blade component like so:

<div>
    <label>...</label>
    <input name="{{ $name }}" type="text">
</div>

You would register this component with BladeX and use it like so:

<input-text name="email">

HOWEVER, if you are using Livewire, you might want to add the wire:model attribute to the input element:

<input-text name="email" wire:model="email">

Unfortunately, BladeX's parser interprets this as an attribute named model that is bound with a PHP expression (because of the colon).

I pulled the project down to add a test for this case, but discovered it's harder than just updating the parser.

You would need a way to make wire:model into a variable $wireModel or something?

Anyhow, I figured I'd open an issue to get the conversation going. It might be an issue outside the scope of this package. But I think this package can pair nicely with Livewire, and this is kind of a deal-breaker.

@driesvints brought this issue to my attention.

Thoughts appreciated. Thanks!

We'll take a look at this soon. We will probably provide an option to ignore parsing certain attributes.

I've made some changes to correctly extract Livewire attributes. Attributes names that contain a colon will no longer be compiled as data binding attributes.

Instead we're passing them to the component's view data as is. This means wire:model='email' will be passed as ['wire:model' => 'email']. However, as it's an illegal variable name, it wont be defined in the view's local variables and it'll only be available through $__data['wire:model'].

This is not ideal so I'm open to any suggestions to get these variables defined in the view's scope.

Going for $wireModel seems like the easiest solution - but maybe putting all the "namespaced" attributes in arrays is more pleasant to use: ($wire['model'])?
That would make it a bit easier to pass all of Livewire attributes straight through in the component's view:

<div>
    <label>...</label>
    <input 
        name="{{ $name }}" 
        type="text"
        {{ collect($wire)->map(fn ($value, $attribute) => "wire:$attribute='$value'")->implode(' ') }}
    >
</div>

Both $wireModel and $wire['model'] are fine by me. Don't have an exact preference.

{{ collect($wire)->map(fn ($value, $attribute) => "wire:$attribute='$value'")->implode(' ') }}

Could be cool to create a helper for this.

gjm commented

If I may step in and give a regular user's perspective, I think the array alternative is better and the example @AlexVanderbist uses shows it perfectly!

Looking forward for this integration to happen because I really love BladeX and Livewire and really want then to get along ๐Ÿ˜„

I really like the $wire['model'] solution. Thanks for digging into this @AlexVanderbist! And to other for weighing in, very exciting.

@AlexVanderbist did you already manage to have a look at this? I see b78a939 but that seems to strip the Livewire attributes?

I've also begun to use Alpine.js in Eventy but it suffers from the same problem as Livewire. Any colon attribute gets stripped:

<div x-data="{ open: false }">
    <x-button text="Open Dropdown" x-on:click="open = true">

    <ul
        x-show="open"
        x-on:click.away="open = false"
    >
        Dropdown Body
    </ul>
</div>

<x-button text="Open Dropdown" x-on:click="open = true"> will compile to <button>Open Dropdown</button>.

If anyone can point me in the right direction I can help out perhaps? I really believe that Livewire + Alpine.js + Blade X are a match made in heaven so would love to see these three work nicely together ๐Ÿ˜„

I solved it for now with this ๐Ÿ˜„ (forgive the ugly oneliner)

@include('components.button', ['text' => 'Change Role', 'alpine' => ['on' => ['click' => 'open = true']]])
{!! collect($alpine ?? [])->map(fn ($value, $directive) => collect($value)->map(fn ($value, $attribute) => "x-$directive:$attribute='$value'")->implode(' '))->implode(' ') !!}

Just out of curiosity, do these solutions work for attribute names that have more than just a colon? Like these:

<div wire:keydown.page-down="foo">
<div wire:poll.750ms>
<div wire:loading.class.remove="bg-blue">
gjm commented

@mokhosh It doesn't... (using 2.4.1) yet!

Support for this has landed in 2.5.0

Thanks all!

Thanks everyone! โค๏ธ

Tested this in Eventy today and it works great ๐Ÿ™

Does this mean that we can use it like @calebporzio's example?

<input-text name="email" wire:model="email">