Skip to content

MudFormComponent: Preserve consumer-managed Error when no validation source#13415

Merged
danielchalmers merged 2 commits into
MudBlazor:devfrom
danielchalmers:fix/validate-preserve-consumer-error
Jul 2, 2026
Merged

MudFormComponent: Preserve consumer-managed Error when no validation source#13415
danielchalmers merged 2 commits into
MudBlazor:devfrom
danielchalmers:fix/validate-preserve-consumer-error

Conversation

@danielchalmers

@danielchalmers danielchalmers commented Jul 2, 2026

Copy link
Copy Markdown
Member

MudFormComponent.ValidateValue (the non-EditContext branch) unconditionally wrote ErrorState/ErrorText from its own error list on every validate pass. When a component has no validation source of its own, that list is empty, so a blur, submit, or value change wiped any Error/ErrorText the consumer had set manually.

The stale comment claimed "setting them here will have no effect" — that was true before Error/ErrorText became ParameterState-backed, but the internal write now wins over the parameter, so it clobbers.

We now publish an internal assessment only when the component actually has a validation source: Validation, Required, For attributes, or a conversion error. A small _hasInternalError flag keeps us writing long enough to clear an error the component previously published (e.g. a resolved conversion error) even after its source goes away. Otherwise the consumer's Error/ErrorText are left intact — the aria error-id, form update, and render still run so a consumer-managed error stays wired up.

Fixes #12732
Fixes #11244
Fixes #4593
Likely also relevant to #7518 and #11503 (same clobber); left open for separate confirmation.

…source

ValidateValue's non-EditContext branch unconditionally wrote ErrorState and
ErrorText from its own (usually empty) error list, so any validate pass -
blur, submit, value change - wiped an Error/ErrorText the consumer set
manually (MudBlazor#12732, MudBlazor#11244, MudBlazor#4593). Since Error/ErrorText became
ParameterState-backed, that write clobbers rather than no-ops as the stale
comment claimed.

Only publish an assessment when the component has a validation source of its
own (Validation, Required, For attributes, or a conversion error), or while
it still needs to clear an error it previously published - tracked by
_hasInternalError so a resolved conversion error still clears. Otherwise the
consumer's Error/ErrorText stay intact.

Adds regression tests for the text-field and select surfaces plus the
conversion-error clear path.
@mudbot mudbot Bot added the bug Unexpected behavior or functionality not working as intended label Jul 2, 2026
@danielchalmers
danielchalmers requested a review from Copilot July 2, 2026 22:40
@danielchalmers

danielchalmers commented Jul 2, 2026

Copy link
Copy Markdown
Member Author

Reproduced live on dev in a Blazor Server harness (real browser interaction), before and after the fix.

Before. A MudTextField with a consumer-managed Error/ErrorText and no Validation/Required/For. Type a value, then blur:

  • consumer model: Error=true, ErrorText="bad"
  • rendered field: no mud-input-error class, no error text, the blur's ValidateValue pass wiped it.

Same on the MudSelect surface (#11244): set a consumer Error, then pick an option - the error styling and text vanished while the consumer's flag was still true.

Root cause. The non-EditContext branch of ValidateValue always ran ErrorState.SetValueAsync(errors.Count > 0) with an empty errors list. Error/ErrorText are ParameterState-backed now, so that internal write outlives the re-supplied parameter and clears the consumer's error. The old comment ("setting them here will have no effect") predates the ParameterState change.

After. Same steps → mud-input-error + aria-invalid="true" + the "bad" helper text now persist through the blur/validate pass.

Edge case guarded against. Gating purely on "has a validation source" would strand a conversion error: it sets ErrorState=true while ConversionError is true, but once the input is corrected ConversionError flips false and the clearing write would be skipped, leaving a stale error. The _hasInternalError flag keeps the component writing long enough to clear its own published error. Covered by ValidateValue_ClearsOwnConversionError_AfterCorrection.

Tests. 3 new regression tests (text field, select, conversion-clear). Full unit suite: 5424 passed / 2 skipped / 0 failed. The 1,254 form-component tests (Select / TextField / NumericField / Autocomplete / pickers / boolean inputs / Form) pass unchanged, since configured fields still hit the exact same write path.

Note: HasErrors reads ErrorState.Value, and on a real Server render a page-level readout can observe it a beat before the child's ParameterState re-applies the Error parameter, so a browser probe momentarily reads the old value even though the input already renders the error. The bUnit assertions read the settled state and are the source of truth here.

This comment was marked as outdated.

Addresses review: _validationAttrsFor is non-null but empty when the
For-targeted property has no ValidationAttribute, so `is not null` still
counted it as a validation source and could wipe a consumer-managed Error.
Require an actual attribute via Any(). Adds a regression test.
@danielchalmers
danielchalmers merged commit 00210fe into MudBlazor:dev Jul 2, 2026
6 checks passed
@danielchalmers
danielchalmers deleted the fix/validate-preserve-consumer-error branch July 2, 2026 22:52
@danielchalmers danielchalmers added the regression Previously worked and now doesn't label Jul 7, 2026
This was referenced Jul 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Unexpected behavior or functionality not working as intended regression Previously worked and now doesn't

Projects

None yet

2 participants