Is there a way to use Blade's attribute-forwarding in Livewire components?

What seems to be the problem:
I want to be able to pass HTML attributes to elements rendered by Livewire components a la Blade, but it doesn’t seem to be possible w/o custom code.

I know I can specify each attribute I want with public properties, but I’d like to avoid that for things like class, name, id, autofocus, etc.

Steps to Reproduce:

  1. Create a simple Livewire component with {{ $attributes }}.
  2. Use it in a template and pass a simple attribute like class="p-2".
  3. See errors.

Are you using the latest version of Livewire:
Yes, just installed 1.2.0 yesterday.

Do you have any screenshots or code examples:
resources/views/livewire/input.blade.php

<input {{ $attributes }}>

app/Http/Livewire/Input.php

<?php

namespace App\Http\Livewire;

use Livewire\Component;

class Input extends Component
{
    public function render()
    {
        return view('livewire.input');
    }
}

Template

<div>
    <livewire:input class="p-2"/>
</div>

Error

Livewire\Exceptions\MountMethodMissingException
Livewire encountered a missing mount method when trying to initialise the [input] component. When passing component parameters, make sure you have a mount method.

…or with a mount method

ErrorException
Undefined variable: attributes (View: resources/views/livewire/input.blade.php) (View: resources/views/livewire/input.blade.php) (View: resources/views/livewire/input.blade.php)

This is not possible by the nature of the Livewire components. Everything you want to reuse from the parent blade you need to pass to the component via mount function and store in public properties of the component.

The reason is very simple - component needs to have everything (all variables) to be re-rendered later as a standalone element. Thus your mount function has to be like:

public function mount( $class , $name , $id , $autofocus, ?array $etc)

Bummer. I suspected that might be the case. I’ll experiment with implementing it another way in a trait or something.

You have to understand one thing about Livewire components - when component rendering itself second time it has a completely different environment without your route, controller and parent blade. Livewire opens the component class and render it. At this point the only thing is available in component are those public properties that you defined and passed via mount method. Thus if you need many properties every time you may consider to pull them directly from the component based on some kind of reference.

I am sure that you will learn very quickly how to use components.

I know there’s more work/experimentation I need to do, but this is looking fairly promising:

Trait for use in Livewire components:

<?php

namespace App\Http\Livewire;

trait ForwardsAttributes
{
    public $attributes;

    public function mount(...$attrs)
    {
        $this->mapAttributes(...$attrs);
    }

    public function mapAttributes(...$attrs)
    {
        $attributes = '';
        collect(...$attrs)->each(function ($value, $attr) use (&$attributes) {
            $attributes .= " {$attr}=\"{$value}\"";
        });
        $this->attributes = $attributes;
    }
}

Livewire component:

<?php

namespace App\Http\Livewire\Fields;

use App\Http\Livewire\ForwardsAttributes;
use Livewire\Component;

class LongformText extends Component
{
    use ForwardsAttributes;

    public $value;

    public function mount($value, ...$attrs)
    {
        $this->value = $value;
        $this->mapAttributes(...$attrs);
    }

    public function updatedValue($value)
    {
        // TODO Refactor to pass the field name as the key.
        $this->emit('fieldUpdated', ['$name' => $value]);
    }

    public function render()
    {
        return view('livewire.fields.longform-text');
    }
}

Livewire component Blade template:

<div>
    <label for="notes" class="block">Notes</label>
    <textarea
        wire:model ="value"
        {!! $attributes !!}
    ></textarea>
</div>

Blade template using Livewire component:

<div class="container mx-auto">
    <div class="w-8/12 mx-auto">
        <livewire:fields.longform-text value="{{ $this->notes }}" :attrs="[
            'class' => 'w-full h-screen mt-1 text-gray-200 form-textarea',
            'name'  => 'notes'
        ]"/>
    </div>
</div>

I arrived here a year later for this nearly identical use case. @curtisblackwell How do you feel now about this approach that you had proposed?

I implemented this on a side project I haven’t touched in forever. I don’t remember if I ran into any issues with it.