How to Pass Attributes to Blade Component
There may be times when you want to pass HTML attributes to your component, for example when you have an anchor component that takes care of ARIA attributes for you:
// a.blade.php
@php
$href = $attributes->get('href');
$role = $attributes->get('role');
$role = $role ?? (isset($href) ? null : 'link');
$ariaDisabled = $attributes->get('aria-disabled');
if (!isset($ariaDisabled)) {
$ariaDisabled = isset($href) ? null : 1;
if ($ariaDisabled === 1) {
$ariaDisabled = $role !== "button" ? "true" : null;
}
}
@endphp
<a {{ $attributes->merge([
'aria-disabled' => $ariaDisabled,
'role' => $role
])
}}>{{ $slot }}</a>
<!-- index.html -->
<nav>
<h2>My website</h2>
<ul>
<li><x-a class="link link--active">Home</x-a></li>
<li><x-a href="/dashboard" class="link">Dashboard</x-a></li>
</ul>
</nav>
Unpacking works on both HTML elements and Blade components, except when nesting in another class-based Blade component. There is no issue when unpacking is used inside anonymous components.
app\
View\
Components\
A.php
Navbar.php
resources\
view\
components\
a.blade.php
footer.blade.php
navbar.blade.php
Using <x-a class="link">
inside <x-footer>
will work without issues, while inside <x-navbar>
you would need to pass attributes as a property.
<!-- footer.blade.php -->
<x-a class="link">About us</x-a>
<!-- navbar.blade.php -->
<x-a :attributes="(new ComponentAttributeBag())->class([ 'link link--active' ])">Home</x-a>
What is the Issue Here?
I haven’t dug much into the internals of the Blade Compiler, but I think it has something to do with parsing raw strings. Here is an excerpt of vendor/laravel/framework/src/Illuminate/View/Compilers
:
// ...
protected function compileOpeningTags(string $value)
{
$pattern = "/
<
s*
x[-:]([w-:.]*)
(?<attributes>
(?:
s+
(?:
(?:
@(?:class)(( (?: (?>[^()]+) | (?-1) )* ))
)
|
(?:
@(?:style)(( (?: (?>[^()]+) | (?-1) )* ))
)
|
(?:
{{s*\$attributes(?:[^}]+?)?s*}}
)
|
(?:
(:\$)(w+)
)
|
(?:
[w-:.@%]+
(
=
(?:
\"[^\"]*\"
|
'[^']*'
|
[^'\"=<>]+
)
)?
)
)
)*
s*
)
(?<![/=-])
>
/x";
return preg_replace_callback($pattern, function (array $matches) {
$this->boundAttributes = [];
$attributes = $this->getAttributesFromAttributeString($matches['attributes']);
return $this->componentString($matches[1], $attributes);
}, $value);
}
// ...
There are 110 matches for attributes
in this file, and some more patterns like this one.
Will it be Fixed?
It hasn’t been fixed yet, and I don’t think it will, because there is a closed issue on Github with the solution of explicitly passing the attributes
property: https://github.com/laravel/framework/issues/47360