1
votes

I have some frequently used complex UI elements built with HTML (such as cards, button groups, etc..) For this purpose, I would like to create reusable blade elements.

The closest solution I found is custom blade directives.

I defined a simple card directive as follows:

Blade::directive('card', function ($expression) {
    list($title, $icon, $buttons) = explode(',', str_replace(["'"], '', $expression));

    $str = '<div class="card card-custom">
                         <div class="card-header">
                             <div class="card-title">
                                 <span class="card-icon">
                                     <i class="'.$icon.'"></i>
                                 </span>
                                 <h3 class="card-label">'.$title.'
                             </div>
                             <div class="card-toolbar">'.$buttons.'
                             </div>
                         </div>
                         <div class="card-body">';
    return "<?php echo '".$str."'; ? >";
});

Blade::directive('endcard', function () {
    return '</div></div>';
})

and in my blade I just call it like this:

@card('Dashboard', 'fa fa-tachometer', $buttonsHtml)
    <div> Card content here...</div>
@endcard

Which would like the attached image.

enter image description here

The problem is that I can't pass any parameter as variable (like $buttonsHtml) because it will take it literally as "$buttonsHtml".

I haven't seen anyone one trying to evaluate the variable before passing it to the custom directive. Is it even possible to do that?

It would be useless if I need to pass the actual value of the $buttonsHtml variable since it is being generated by some other function. Any one has an idea of a work around, or even a substitution to using custom directives?

3

3 Answers

4
votes

Laravel 7 introduced Blade components. It allows you to do just that. See here: https://laravel.com/docs/7.x/blade#components.

These components can be called using HTML-like tags and you can pass down data.

In your case this might look like this:

That's how you'd call your component

<x-card title="Dashboard" icon="fa fa-tachometer">
    <x-slot name="buttons">
        Your buttons HTML here
    </x-slot>
    Card content here
</x-card>

resources/views/components/card.blade.php

<div class="card card-custom">
    <div class="card-header">
        <div class="card-title">
            <span class="card-icon">
                <i class="{{ $icon }}"></i>
            </span>
            <h3 class="card-label">{{ $title }}</h3>
        </div>
        <div class="card-toolbar">
            {{ $buttons }}
        </div>
    </div>
    <div class="card-body">
        {{ $slot }}
    </div>
</div>

app/View/Components/Card.php

class Card extends Component
{
    /** @var string Card title */
    public $title;

    /** @var string Card icon */
    public $icon;
    
    public function __construct($title, $icon)
    {
        $this->title = $title;
        $this->icon = $icon;
    }

    public function render()
    {
        return view('components.card');
    }
}
3
votes

You can use Blade components to create reusable html elements and because your card element is pretty simple, I would use a anonymous component.

Create the following file:

"resources/views/components/card.blade.php"

@props([
'title' => '',
'icon' => '',
])

<div class="card card-custom">
    <div class="card-header">
        <div class="card-title">
            <span class="card-icon"><i class="{{ $icon }}"></i></span>
            <h3 class="card-label">{{ $title }}</h3>
        </div>
        @if(isset($toolbar))
            <div class="card-toolbar">{{ $toolbar }}</div>
        @endif
    </div>
    <div class="card-body">{{ $slot }}</div>
</div>

Now you can use it like this in your view files:

<!-- without toolbar -->
<x-card title="Dashboard" icon="fa fa-tachometer">
    <div> Card content here...</div>
</x-card>

<!-- with toolbar -->
<x-card title="Dashboard" icon="fa fa-tachometer">
    <x-slot name="toolbar">
        {!! $buttonsHtml !!}
    </x-slot>

    <div> Card content here...</div>
</x-card>
0
votes

It is an unique and interesting approach for sure. Try writing it in quotes without the '$' sign, like :

`@card('Dashboard', 'fa fa-tachometer', 'buttonsHtml')
    <div> Card content here...</div>
@endcard`

It might work