Let's imagine I have a blade component such as:
<input type="text" id="foo" name="{{$name}}"/>
<script>
$('#foo').whatever
</script>
IRL, this component is far more complicated. You could imagine a long form with plenty of fields with generic names like name
, description
, owner
...
Then I use this component multiple times, again I propose a foreach
but we could imagine something else in which I cannot use an iterator to provide an id to my component:
@foreach($items as $item)
@component('foo')
@endcomponent
@endforeach
How can I ensure the uniqueness of the id
?
One perhaps bad solution would be to use a local variable:
@php($id = uniqid())
<input type="text" id="{{$id}}" name="{{$name}}"/>
<script>
$('#{{$id}}').change(whatever)
</script>
Is there a better way?
I'd pass the item to the component:
@component('foo', ['item' => $item])
Which allows you to do this in it:
<input type="text" id="foo_{{ $item->id }}"/>
Had the same need, found this answer, and as the OP said, I prefer to think that since the components needs it, it should handle it.
I came up with this alternative.
// resources/views/components/button.blade.php
@php
$yourLibNameId = uniqid();
@endphp
<button data-your-lib-name-id="{{ $yourLibNameId }}">{{ $slot }}</button>
@push("script")
<script type="text/javascript" defer="true">
document.addEventListener("DOMContentLoaded", function() {
const button = document.querySelector("[data-your-lib-name-id='{{ $yourLibNameId }}']");
if (button instanceof HTMLElement) {
button.addEventListener("click", function() {
console.log("clicked");
});
// or do whatever needed to initialize the button...
}
});
</script>
@endpush
To summarize:
id
attribute, I use a data-x
attribute to not interfer with the id, and give a proper scope to the use of this data@push("script")
directive at the right place in his layout or viewI am about to create a component library for Material Components Web, so this is what I will use to proceed (hence the your-lib-name
).
Hope it help folks keeping their components the most isolated possible.
If we can assume the ID only needs to be referenced within the component (e.g. a field and a label together) and it doesn't matter anywhere else what the ID is as long as its unique... uniqid looks like it should be fine, but you could also use \Illuminate\Support\Str::random()
as part of the ID. Obviously either of those would change with every render, and I would personally favor including some meaningful text as well (e.g. 'name_field_'.Str::random()
).
Alternatively, a singleton could be used to generate and track IDs used and make sure they're not reused. Something like:
class UniqueIdTracker
{
protected $idTracking = [];
public function __invoke($prefix)
{
$increment = $this->idTracking[$prefix] ?? 0;
$this->idTracking[$prefix]++;
return $prefix . $increment;
}
}
Which would then be called with app(\Namespace\UniqueIdTracker::class)('prefix_')
. (This could be improved by wrapping it in a helper function or something).
I would also note that AlpineJS would probably be a good fit for OP's use case, as the script can attach to the element without ID.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With