Laravel Blade Component - Pass property with colon in the middle and access from class

Viewed 516

I have the following code in a Livewire Component

<x-input.color
    wire:model="foo.name"
    wire-test="foo.name"
/>

So I am trying to use a Blade Component and pass both properties to the class.

class Color extends Component
{
    public $wireTest;
    public $wireModel;
    
    public function __construct($wireTest = null, $wireModel = null)
    {
        $this->wireTest = $wireTest; // will resolve
        $this->wireModel = $wireModel; // wont resolve
        dump($this->data()); // will resolve the attribute bag thats empty
    }
    
    public function render()
    {
    dump($this->data()); // will resolve the attribute bag thats empty
         return view('components.input.color');
    }

}

color.blade.php

<input {{$attributes}} ... />
//$attributes will contain 'wire:model' => 'foo.name'

I cannot figure out how to pass access a property with a : in the middle of the name: ie wire:model within the class

Is there anyway I can access the wire:model attribute within the class?

2 Answers

You don’t have to pass a variable with a : in the attribute:

return view('components.input.color', [‘model’ => $this->wireModel, ‘test’ => $thid->wireTest]);

And then in your test use those as followed

<input wire:model=“{{ $model }}” wire-test=“{{ $test }}” />

Edit:

I've packaged the solution below into stephancasas/blade-wants-attributes.


I realize this question is old, but in case you or anyone else still needs an answer, I did find a solution. My use case is for package development — building components which universally work with Blade, Livewire, and AlpineJS. In order to do this cleanly, it's imperative that I'm able to evaluate all component attributes within the class constructor.

My solution was to write and register a Blade pre-compiler:

namespace StephanCasas\WireWorkbench\Support;

use Illuminate\View\Compilers\ComponentTagCompiler;
use Illuminate\Support\Str;

class ClassComponentAttributeHydrator extends ComponentTagCompiler
{
    public function compile(string $value)
    {
        $glue = '##BEGIN-COMPONENT-CLASS##';

        return collect(
            preg_split('/' . $glue . '/s', $value)
        )->transform(function ($instance) use ($glue) {
            $attrs = [];

            preg_match(
                '/' . preg_quote('<?php $component->withAttributes(') . '.*/',
                $instance,
                $attrs
            );

            if (!count($attrs)) return $instance;

            $attrs = (string)Str::of($attrs[0])->after('(')->beforeLast(')');

            return "<?php \$__env->share('__attributes', $attrs); ?>\n$glue$instance";
        })->join('');
    }
}

Then, in my package service provider's boot() method, I can register the pre-compiler:

if (method_exists($this->app['blade.compiler'], 'precompiler')) {
    $this->app['blade.compiler']->precompiler(function ($string) {
        return app(ClassComponentAttributeHydrator::class)
            ->compile($string);
    });
}

Now, from within the class component, I can assign or access the attributes as needed:

public function __construct() {
    $this->attributes = $this->newAttributeBag(
        app('view')->shared('__attributes')
    );

    if ($this->attributes->has('wire:model')) {
        // ... do something
    }
}

In order to understand how this works, you'll need to take a look at the way Blade pre-compilers work, and how that relates to the structure of cached view output. It will also be helpful to study the lifecycle of View\Factory.

If a problem emerges with this solution, I'll update my remarks.

Cheers.

Related