Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to ensure unique ID in Laravel Blade?

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?

like image 539
nowox Avatar asked Jun 05 '19 18:06

nowox


3 Answers

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 }}"/>
like image 167
ceejayoz Avatar answered Nov 20 '22 03:11

ceejayoz


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:

  • Instead of relying on an 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
  • I assume the user will provide a @push("script") directive at the right place in his layout or view

I 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.

like image 43
Anwar Avatar answered Nov 20 '22 03:11

Anwar


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.

like image 1
Chris Tyler Avatar answered Nov 20 '22 04:11

Chris Tyler