7

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?

Dan
  • 115
  • 1
  • 8
  • what is your class name? – TEFO Apr 28 '20 at 21:56
  • The class is called "Input" – Dan Apr 29 '20 at 06:28
  • no harm in test so update your laravel to 7.9 and your code be like this: if it didnt work remove colon before title – TEFO Apr 29 '20 at 13:10
  • The field renders fine in a Blade template, I need to know how to render it programmatically. I'm creating a system to let the user create their own fields and I need to render them from code rather than a fixed template. – Dan Apr 29 '20 at 13:26

6 Answers6

10

Try this, it works for me in laravel 8, and I checked data function exists in laravel 7

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

** data function include methods, properties and attributes of Component.

Huynh Trinh
  • 101
  • 1
  • 3
  • 1
    this is amazing! thank you! for my use case i wanted the output as a string so i did: `$pre_rendered_component_as_html_string = $header_component->render()->with($header_component->data())->render();` – jake downs Oct 21 '21 at 06:21
  • @jakedowns I needed too, but I found it more poetic to call `toHtml()` at the end, which does exactly the same in Laravel 8 at least :D – Luciano Fantuzzi Oct 31 '21 at 23:15
5

The last release (v.8.80) of Laravel should help you achieve what you're trying to do with the pull #40425

Blade::render('<x-input name="name" :title="$title" />', ['title' => __('My field')]);
Clément Baconnier
  • 5,718
  • 5
  • 29
  • 55
4

This is now possible using the Blade::renderComponent method. So this is how you should do it:

echo Blade::renderComponent(new Input('name', 'My field'));
JozyDaPozy
  • 61
  • 6
2

Manually add variables to the view. Otherwise it won't work

I reported this but it's not considered an issue:

https://github.com/laravel/framework/issues/32429

lalo
  • 901
  • 2
  • 10
  • 15
1

Just add the properties to view manually and it will work. If not make the properties private but it should not have influence to that. Just my implementation.

     /**
     * Get the view / contents that represent the component.
     *
     * @return \Illuminate\View\View|string
     */
    public function render()
    {
        return view(
            'components.fields.input',
            ['name' => $this->name, 'title' => $this->title]
        );
    }
Marek Barta
  • 344
  • 1
  • 4
  • 17
1

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',
]);
Daffa Mumtaz
  • 24
  • 2
  • 3