5

In Blazor, I might often have the same component render on a page twice. Inside this component, I might have <label for="foo"> that refers to an <input id="foo"> in the same component.

Is there a convenient way to set the two IDs to different values per component, but the same value within the component? If each component had a parameter that was different, we could use that, but what about in the case where there is no difference between the parameters' values?

I think I'd have to declare a private instance value, say private Guid ComponentInstanceId {get;} = Guid.NewGuid(); and use that with <label for="foo-@ComponentInstanceId">...<input id="foo-@ComponentInstanceId">, but maybe there's a better way?

Patrick Szalapski
  • 8,738
  • 11
  • 67
  • 129
  • 1
    The best way is to simply nest your `for` target inside the label. See https://stackoverflow.com/questions/8537621/possible-to-associate-label-with-checkbox-without-using-for-id/8537641#8537641 – Kirk Woll Apr 27 '21 at 21:29
  • That isn't always desirable, such as when the CSS doesn't match that practice, or when there are other elements between the label and input. But I do prefer that way when it is feasible. – Patrick Szalapski Apr 27 '21 at 22:07
  • Fair enough, I only mentioned it in case it could help you in this case. Other than that, since ids must be unique, I think the option like you outlined is the only practical solution. – Kirk Woll Apr 27 '21 at 22:12
  • Is `@key` an option? https://learn.microsoft.com/en-us/aspnet/core/blazor/components/?view=aspnetcore-5.0#use-key-to-control-the-preservation-of-elements-and-components – Steve Greene Apr 28 '21 at 15:21
  • 1
    Patrick - your approach creating a new private GUID is the same conclusion my team came to, we haven't had any problems with it - I'd say that's probably your best bet. (Though if you find a better one, please share!) – JeremyW Apr 30 '21 at 21:00
  • 1
    Just be sure your generated id starts with a letter. The Guid() is a string that starts with digits. The querySelector method uses CSS3 selectors for querying the DOM and CSS3 doesn't support ID selectors that start with a digit. – Graham Epps Jan 30 '22 at 04:17
  • 1
    This is a pretty disappointing oversight on the part of the Blazor team! – Chris Bordeman Feb 14 '22 at 02:48
  • @KirkWoll unfortunately [nesting within label is incompatible](https://twitter.com/aardrian/status/1188470905502130176) with some assistive technology. It's ass but what can you do. – Molly Stewart-Gallus Jun 17 '23 at 22:27

1 Answers1

4

Instead of using <input> directly, I typically have my own custom input components (MyDate, MySelect etc). And I use GetHashCode() of the component instance for this, here's partial code for MySelect.razor:

<label for="@this.GetHashCode()">@Label</label>
<select id="@this.GetHashCode()" @bind=Value>
...
</select>

And then I invoke them without worrying about id:

 <MySelect Label="Source of Change" @bind-Value=ChangeOrder.SourceOfChange />

It seems to be reliable so far, with every component on the page having unique id, but it's technically not guaranteed unique by .net for all cases. You may want to provide your own GetHashCode or other function, e.g. based on position in the document tree.

...Or simply use Guid.NewGuid() but use the same guid in both label and input.

Ekus
  • 1,679
  • 21
  • 17
  • 2
    Using GetHashCode is inviting trouble. As you say, GetHashCode is not guaranteed to be unique. You might face very-hard-to-track-down bugs in the future. – avl_sweden Dec 12 '22 at 07:52
  • I agree @avl_sweden, thus the warning. A private property with a guid would be a simple improvement. Still, a duplicated ID in – Ekus Dec 13 '22 at 06:43