Search code examples
laravellaravel-bladelaravel-7

Render Laravel 7 component programmatically


I have a Laravel 7 component which looks like this

class Input extends Component
{
    public $name;
    public $title;
    public $value;
    public $type = 'text';

    /**
     * Create a new component instance.
     *
     * @return void
     */
    public function __construct($name, $title)
    {
        $this->name = $name;
        $this->title = $title;
        $this->value = \Form::getValueAttribute($name);
    }

    /**
     * Get the view / contents that represent the component.
     *
     * @return \Illuminate\View\View|string
     */
    public function render()
    {
        return view('components.fields.input');
    }
}

I can render the field in my Blade component like this:

<x-input name="name" :title="__('My field')" />

I have a requirement to create and render the field in code, I've tried the following:

$field = new Input('name', 'My field');
$field->render();

This returns an error:

Undefined variable: title

I can see that the render function is called but the public properties are not made available to the view. How would I render the component with the public properties?


Solution

  • I use this, it also works with anonymous component. Put this in your helper file.

    /**
     * Render a blade component.
     * @param string $name Component name, exclude component folder (i.e use "card" instead of "components.card")
     * @param array $props Component properties
     * @param array $attributes Component attributes
     * @return \Illuminate\Contracts\View\View
     */
    function viewComponent($name, $props = [], $attributes = []) {
        $className = collect(explode('.', $name))->map(function($part) {
            return \Str::studly($part);
        })->join('\\');
        $className = "App\\View\\Components\\{$className}";
        if(class_exists($className)) {
            $reflection = (new \ReflectionClass($className))->getConstructor();
            $parameters = [];
            foreach ($reflection->getParameters() as $param) {
                $parameters[] = $props[$param->name] ?? $param->getDefaultValue();
            }
            $component = new $className(...$parameters);
            $component->withAttributes($attributes);
            return $component->render()->with($component->data());
        }
    
        $props['attributes'] = new \Illuminate\View\ComponentAttributeBag($attributes);
        return view("components.$name", $props);
    }
    

    Usage

    viewComponent('input', [
        'name' => 'name',
        'title' => 'My Field',
    ]);
    
    // if you want custom attribute
    viewComponent('input', [
        'name' => 'name',
        'title' => 'My Field',
    ], [
        'autocomplete' => 'off',
        'data-custom' => 'custom attribute',
    ]);