laravel / ideas

Issues board used for Laravel internals discussions.
938 stars 31 forks source link

Rendering Blade Components Outside of Views #2649

Open godismyjudge95 opened 3 years ago

godismyjudge95 commented 3 years ago

Sometimes one might have need of rendering a blade component directly from say a controller or maybe a customized component instance from a model method.

For example:

public function getIconAttribute()
{
    return view('components.icon', ['name' => $this->icon_name]);
}

This works for simpler components; however, if the component looks like the following, it fails:

@props(['name'])
<i {{ $attributes->merge(['class' => 'icon']) }} data-icon="{{ $name }}"></i>

The reason why this fails is because the $attributes variable is never instantiated when the component is rendered directly. To get around this one might just pass an attribute variable from the view method, however this also fails due to the attributes variable needing to be an instance of ComponentAttributeBag.

My current solution - albeit a poor one - is to have a helper method instantiate an AnonymousComponent and then proceed to render that:

function view_component(string $view, array $slot, array $attributes): View
{
    $component = new AnonymousComponent($view,  compact('slot'));
    $component->withAttributes($attributes);

    return view($component->render(), $component->data());
}

It would be nice if there was a built in solution to this use case and one that also allowed for rendering of class based components.

I realize this might be a really niche case, but I have not found any other way to re-use the code from the blade components outside a view file.

tobyzerner commented 3 years ago

Keen for this. What would we want the API to look like? I'm not sure it warrants a whole new helper, but maybe a new method on the Illuminate\View\Factory class, so it could be used like this:

public function getIconAttribute()
{
    return view()->component('icon', ['name' => $this->icon_name]);
}

This new Illuminate\View\Factory::component($name, $attributes, $slots) method would ultimately return a string via the existing renderComponent method.