Blazor's binding syntax is awkward and inconsistent. I've read the documentation for data binding and there's no simple way to bind the result of a "true" or "false" condition. In the code below, the "aria-pressed" attribute is required and must be true or false.
<div class="btn-group-vertical" name="Toolbar" role="toolbar" aria-label="Toolbar containing tools">
@foreach (var tool in _tools)
{
<button @key="tool" aria-pressed="@(_selectedToolIndex == toolIndex)" class="btn btn-outline-primary shadow-none @(_selectedToolIndex == toolIndex ? "active" : "")"
enabled="@tool.Enabled" role="button" tabindex="@toolIndex">
<i class="fas [email protected]"></i>
</button>
toolIndex++;
}
</div>
Blazor's binding, will correctly output,
aria-pressed="true"
but will not output the
aria-pressed="false".
This makes it difficult to implement ARIA. Oddly enough, if you apply this to css classes like so:
class="btn btn-outline-primary shadow-none active=@(_selectedToolIndex == toolIndex)
the binding behaves as you would expect, but not in the way that you want. It outputs:
class="btn btn-outline-primary shadow-none active="True"
or
class="btn btn-outline-primary shadow-none active="False"
Clearly, the binding syntax needs a consistent syntax and behavior. I was hoping the mysterious @bind
would come to the rescue, but it's context dependent and incomplete. It would be nice if we could use something like:
<button @key="tool" @bind-attr(tag: "aria-pressed", value: _selectedToolIndex == toolIndex, required: true)" class="btn btn-outline-primary shadow-none @bind-class(class: "active", value: _selectedToolIndex == toolIndex, required: false)"
enabled="@tool.Enabled" role="button" tabindex="@toolIndex">
While a bit more verbose, its behavior is clear and consistent. The "required" parameter determines whether the result can be omitted if the value evaluates to true, null or empty string. Alternatively, you could make any class or attributed required by some annotation such as:
<button @key="tool" [aria-pressed="@(_selectedToolIndex == toolIndex)"] class="btn btn-outline-primary shadow-none @(_selectedToolIndex == toolIndex ? "active" : "")"
enabled="@tool.Enabled" role="button" tabindex="@toolIndex">
<i class="fas [email protected]"></i>
</button>
In this case, the [] tells the razor the output is required.
This is a razor feature since some (long?) time, a html property that is being assigned a C# boolean value means to omit the attribute if false
, or display the attribute on true
. You can cheat this system by calling .ToString()
on the boolean, this is also why it works in your sample with the class
attribute where the bool
is implicitly converted to string
.
Requiring a cheat is not my idea of a good design. Unlike the way binding works in the rest of the .NET platform, Blazor's binding depends multiple rules of where and how they are applied. To understand them requires an understanding of its underlying implementation, rendering it a leaky abstraction. An experienced .NET developer should be able to apply their existing knowledgeof data binding and instantly get up to speed.
it is in the documentation https://docs.microsoft.com/en-us/aspnet/core/blazor/components?view=aspnetcore-3.0 (sub title "Conditional HTML element attributes"). If it's not clear enough, you can edit it here https://github.com/aspnet/AspNetCore.Docs/blob/master/aspnetcore/blazor/components.md.
It was already the case in razor since many versions.
We've moved this issue to the Backlog milestone. This means that it is not going to happen for the coming release. We will reassess the backlog following the current release and consider this item at that time. However, keep in mind that there are many other high priority features with which it will be competing for resources.
Would a suitable fix for this to be to output aria-* attributes even when their value is false?
Would a suitable fix for this to be to output aria-* attributes even when their value is false?
Yes. The aria-* attributes are required if they support being pressed.
https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/button_role
Associated ARIA roles, states, and propertiesSection
aria-pressed
Defines the button as a toggle button. The value of aria-pressed describes the state of the button. The values include _aria-pressed="false"_ when a button is not currently pressed, _aria-pressed="true"_ to indicate a button is currently pressed, and _aria-pressed="mixed"_ if the button is considered to be partially pressed. If the attribute is omitted or set to its default value of _aria-pressed="undefined"_, the element does not support being pressed.
ToString() solved it to me.