Skip to content

[Blazor] InputBase does not update FieldIdentifier when bound model instance changes #64134

@alexey-andriyanenko

Description

@alexey-andriyanenko

Is there an existing issue for this?

  • I have searched the existing issues

Is your feature request related to a problem? Please describe the problem.

Description

When using Blazor WASM form validation with complex nested objects that may replace instances at runtime,
inputs derived from InputBase<TValue> can display stale UI and validation state.

InputBase.SetParametersAsync assigns its FieldIdentifier only once on initialization. If the underlying FieldIdentifier.Model object instance changes, the component keeps using the old (Model, FieldName) pair. As a result, EditContext.GetValidationMessages() may return an empty set,
but the input remains visually invalid because its cached FieldIdentifier still points to the previous instance.

Reproduction example

Let's say we have a checkout form where user has to enter amount and select a payment method.
Models would look like this.

public class CheckoutForm
{
    public PaymentMethod Payment { get; set; }
}

public abstract class PaymentMethod
{
    [Required]
    [Range(1, 999, ErrorMessage = "Amount must be between 1 and 999.")]
    public int Amount { get; set; }
}

public class CardPayment : PaymentMethod
{
    // whatever fields for card payment
}

public class PayPalPayment : PaymentMethod
{
    // whatever fields for paypal payment
}

And a code that performs payment method change:

private void HandlePaymentMethodChange(PaymentType type) {
    if (type == PaymentType.Card) {
        FormModel.Payment = new CardPayment {
             Amount = FormModel.Payment.Amount
        }
    }

   if (type == PaymentType.PayPal) {
       FormModel.Payment = new PayPalPayment {
            Amount = FormModel.Payment.Amount
       }
    }
}

Steps to reproduce:

  • Enter an invalid value in Amount (e.g., value less than 1 or more than 999).
  • Switch from CardPayment to PayPalPayment the Payment property reference changes).
  • Enter a valid numeric value.

Expected
The input should reflect the new, valid state of the new Payment instance.

Actual
The EditContext has no validation messages, but the input still displays as invalid.
The old FieldIdentifier is still used by the input component.

Current workaround

Usage of key attribute with a value of model instance resolves issue because each time model is changed, the subtree is fully re-created leading to re-initialization of FieldIdentifiers.

<EditContext>
    <PaymentMethodComponent @key="@FormModel.Payment" />
</EdiContext>

Describe the solution you'd like

The solution would be to update InputBase.SetParametersAsync code by providing additional check for reference equality between previous FieldIdentifier.Model and current one.

public override Task SetParametersAsync(ParameterView parameters)
{
    // ... old code

    // NEW: detect instance swap for the owner of ValueExpression's final member
    if (ValueExpression is not null)
    {
        var newField = FieldIdentifier.Create(ValueExpression);

        // If the owning instance or the field changed, switch identifiers
        if (!ReferenceEquals(newField.Model, FieldIdentifier.Model))
        {
            // Clear parsing messages we (this input) may have added for the OLD field
            if (_parsingValidationMessages is not null)
            {
                _parsingValidationMessages.Clear(FieldIdentifier);
                EditContext?.NotifyValidationStateChanged();
            }

            // Switch to NEW field
            FieldIdentifier = newField;

            // Reset parsing/display state so we don't carry stale UI forward
            _parsingFailed = false;
            _previousParsingAttemptFailed = false;
            _incomingValueBeforeParsing = null;
        }
    }
}

Additional context

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    Needs: Author FeedbackThe author of this issue needs to respond in order for us to continue investigating this issue.area-blazorIncludes: Blazor, Razor Components

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions