0

I would like to create one "parent" component class that can render multiple different views based on which "child" view you pass in.

For example, I would like to create a directory resources/views/components/icon where I could put SVGs:

resources/views/components/icon/delete.blade.php:

@if ($outline)
    <svg dusk="icon-delete" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" {{ $attributes->class(['h-6' => !$heightOverride, 'w-6' => !$widthOverride]) }}>
        <path stroke-linecap="round" stroke-linejoin="round" d="M14.74 9l-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 013.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 00-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 00-7.5 0" />
    </svg>
@elseif ($mini)
    <svg dusk="icon-delete" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" {{ $attributes->class(['h-5' => !$heightOverride, 'w-5' => !$widthOverride]) }}>
        <path fill-rule="evenodd" d="M8.75 1A2.75 2.75 0 006 3.75v.443c-.795.077-1.584.176-2.365.298a.75.75 0 10.23 1.482l.149-.022.841 10.518A2.75 2.75 0 007.596 19h4.807a2.75 2.75 0 002.742-2.53l.841-10.52.149.023a.75.75 0 00.23-1.482A41.03 41.03 0 0014 4.193V3.75A2.75 2.75 0 0011.25 1h-2.5zM10 4c.84 0 1.673.025 2.5.075V3.75c0-.69-.56-1.25-1.25-1.25h-2.5c-.69 0-1.25.56-1.25 1.25v.325C8.327 4.025 9.16 4 10 4zM8.58 7.72a.75.75 0 00-1.5.06l.3 7.5a.75.75 0 101.5-.06l-.3-7.5zm4.34.06a.75.75 0 10-1.5-.06l-.3 7.5a.75.75 0 101.5.06l.3-7.5z" clip-rule="evenodd" />
    </svg>
@else
    <svg dusk="icon-delete" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" {{ $attributes->class(['h-6' => !$heightOverride, 'w-6' => !$widthOverride]) }}>
        <path fill-rule="evenodd" d="M16.5 4.478v.227a48.816 48.816 0 013.878.512.75.75 0 11-.256 1.478l-.209-.035-1.005 13.07a3 3 0 01-2.991 2.77H8.084a3 3 0 01-2.991-2.77L4.087 6.66l-.209.035a.75.75 0 01-.256-1.478A48.567 48.567 0 017.5 4.705v-.227c0-1.564 1.213-2.9 2.816-2.951a52.662 52.662 0 013.369 0c1.603.051 2.815 1.387 2.815 2.951zm-6.136-1.452a51.196 51.196 0 013.273 0C14.39 3.05 15 3.684 15 4.478v.113a49.488 49.488 0 00-6 0v-.113c0-.794.609-1.428 1.364-1.452zm-.355 5.945a.75.75 0 10-1.5.058l.347 9a.75.75 0 101.499-.058l-.346-9zm5.48.058a.75.75 0 10-1.498-.058l-.347 9a.75.75 0 001.5.058l.345-9z" clip-rule="evenodd" />
    </svg>
@endif

And somewhere in a blade I could do this:

<button onclick="deleteItem(this)">
    <x-icon.delete mini class="h-4 w-4 mr-2 text-gray-400">
    <span>Delete</span>
</button>

The problem is, a component like the following doesn't work -- it never gets called. It seems like I would need a component for every icon (i.e. Icon.delete.php). I would like to make one component called app/View/Components/Icon.php that gets called every time you reference an x-icon.{icon_name} in a blade:

<?php

/* THIS DOESN'T WORK */

namespace App\View\Components;

use Illuminate\View\Component;

class Icon extends Component {
    public bool $outline;
    public bool $mini;
    public bool $heightOverride;
    public bool $widthOverride;

    /**
     * Create a new component instance.
     *
     * @return void
     */
    public function __construct(bool $outline = false, bool $mini = false) {
        $this->outline = $outline;
        $this->mini = $mini;
    }

    /**
     * Get the view / contents that represent the component.
     *
     * @return \Illuminate\Contracts\View\View|\Closure|string
     */
    public function render() {
        return function (array $data) {
            $this->heightOverride = (bool)preg_match('/(?:\sh-|^h-)/', $data['attributes']['class']);
            $this->widthOverride = (bool)preg_match('/(?:\sw-|^w-)/', $data['attributes']['class']);

            $index = strpos($data['componentName'], '.');
            $name = subtr($data['componentName'], $index);
            return view("components.icon." . $name);
        };
    }
}

Is it possible to make one component class like the above example such that it gets called every time you reference a <x-icon.{icon_name} /> in a blade?

RileyS
  • 11
  • 4
  • You may just need an [anonymous component](https://laravel.com/docs/9.x/blade#components), and as pass the mini/outilne as [attributes](https://laravel.com/docs/9.x/blade#passing-data-to-components) – aynber Aug 24 '22 at 20:22

1 Answers1

1

In the view pass the component name:

<x-widget :view="my_view_name" />

In the compnent controller:

 public function __construct($view, $data){
    $this->view= $type;
    
    $this->data = 'needed or common data goes here';
}

public function render(){
   return view('components.widget-' . $this->view, $this->data);
}

And you can use the data is the compnent view::

<div class="widget">
   <?= $data; ?>
</div>

I would recommend this though only if you have blocks of code that are common between the components that you can set in the constructor function. And do not want to repeat the same block of code inside each component view. Otherwise it would be better to use anonymous components.

dg_lucian
  • 63
  • 8
  • I think invoking the widget would be either `` or ``. The ":" treats the content is of the attribute as PHP, rather than just a string. – Jason Feb 23 '23 at 15:01