9

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?

4
  • what is your class name? Commented Apr 28, 2020 at 21:56
  • The class is called "Input" Commented Apr 29, 2020 at 6:28
  • no harm in test so update your laravel to 7.9 and your code be like this: <x-input name="name" :title="{{ __('My field') }}" /> if it didnt work remove colon before title Commented Apr 29, 2020 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. Commented Apr 29, 2020 at 13:26

6 Answers 6

11

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')]);
Sign up to request clarification or add additional context in comments.

Comments

11

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'));

Comments

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.

2 Comments

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();
@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
2

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',
]);

Comments

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

Comments

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]
        );
    }

1 Comment

This way you lose the logics inside app/View/Components/*

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.