Files
radzen-blazor/Radzen.Blazor/RadzenTextBox.razor
Jan-Willem Spuij 3690e80672 Sync DOM input with bound value on RadzenMask/RadzenTextBox (Blazor 10)
Both components used the pre-.NET-7 binding pattern
`value="@Value" @onchange="@OnChange"` on their <input>. Per the ASP.NET
docs, that approach "can cause synchronization errors, where the <input>
element displays a different value than the value held in the bound
variable. Migrate to `:get`/`:set` modifiers to avoid this issue." On
.NET 10's renderer the issue is consistently reproducible: when a
parent does not accept the user-typed value (e.g. a downstream parse
fails and the bound nullable stays at null), Blazor's diff does not
re-write the value attribute because the rendered parameter is
unchanged across renders; the DOM keeps the user-typed text.

Migrate both components to `@bind:get`/`@bind:set` with
`@bind:event="onchange"`. The set callback (SetValue/UpdateValue) no
longer assigns the local Value field — the @bind framework's
post-handler sync relies on the bound .NET value remaining at the
parent's value to detect the divergence with the DOM and force-write.
ValueChanged still fires; the parent's two-way binding (or lack of
it) determines what Value becomes on the next parameter assignment.

Existing RadzenTextBox trim tests that asserted `Instance.Value` after
a raw `Add(p => p.ValueChanged, ...)` setup are updated to use a small
wrapper component that performs the binding properly (otherwise Value
no longer round-trips via the parameter). New regression tests
cover the parent-rejection scenario for both components.
2026-07-02 08:14:06 +03:00

14 lines
997 B
Plaintext

@inherits FormComponentWithAutoComplete<string>
@if (Visible)
{
if (Immediate)
{
<input @ref="@Element" id="@GetId()" disabled="@Disabled" readonly="@ReadOnly" name="@Name" style="@Style" @attributes="Attributes" class="@GetCssClass()" tabindex="@(Disabled ? "-1" : $"{TabIndex}")"
placeholder="@CurrentPlaceholder" maxlength="@MaxLength" autocomplete="@AutoCompleteAttribute" aria-autocomplete="@AriaAutoCompleteAttribute" @bind:get="@Value" @bind:set="@SetValue" @bind:event="oninput" @onchange="@OnChange"/>
}
else
{
<input @ref="@Element" id="@GetId()" disabled="@Disabled" readonly="@ReadOnly" name="@Name" style="@Style" @attributes="Attributes" class="@GetCssClass()" tabindex="@(Disabled ? "-1" : $"{TabIndex}")"
placeholder="@CurrentPlaceholder" maxlength="@MaxLength" autocomplete="@AutoCompleteAttribute" aria-autocomplete="@AriaAutoCompleteAttribute" @bind:get="@Value" @bind:set="@SetValue" @bind:event="onchange" />
}
}