Wrapping Blazor EditForm not updating Model

1.1k Views Asked by At

I want to wrap Blazor's EditForm component so that I can add some additional functionality.

Also, I'm creating input components that inherit from InputBase<T> for added features/functionality/ease of use.

When I use my custom inputs in Blazor's EditForm, the Model gets updated properly. However, when I use my wrapper for EditForm, the Model does not get updated. If I use Blazor's InputText component in my wrapper, the Model gets updated properly.

The code (edited for brevity):

BfForm.razor (the wrapper):

@using Microsoft.AspNetCore.Components.Forms

<EditForm Model="Model" OnInvalidSubmit="OnInvalidSubmit" OnValidSubmit="OnValidSubmit" novalidate>
    <DataAnnotationsValidator />
    <ValidationSummary />
    @ChildContent
</EditForm>

@code{
    //This displays the ChildContent as expected, validation works, but the Model does not get updated
    [EditorRequired, Parameter] public RenderFragment ChildContent { get; set; }
    
    // When I use this, I get a .ToString(), i.e.
    // Microsoft.AspNetCore.Components.RenderFragment`1[Microsoft.AspNetCore.Components.Forms.EditContext]
    //[EditorRequired, Parameter] public RenderFragment<EditContext> ChildContent { get; set; }
    [EditorRequired, Parameter] public Object Model { get; set; }
    [EditorRequired, Parameter] public EventCallback OnInvalidSubmit { get; set; }
    [EditorRequired, Parameter] public EventCallback OnValidSubmit { get; set; }
}

BfText.razor:

@inherits InputBase<string>

@* I've tried using Microsoft's InputText with @bind-Value and @bind as well as @bind-value *@
<input 
    type="text"
    @attributes="AdditionalAttributes"
    @bind-value="CurrentValue" />

@code {
    protected override bool TryParseValueFromString(string value, out string result, out string validationErrorMessage)
    {
        result = value;
        validationErrorMessage = null;
        return true;
    }
}

And the usage:

<BfForm Model="model" OnInvalidSubmit="invalidSubmit" OnValidSubmit="validSubmit">

    <BfText DisplayName="First name" @bind-Value="model.FirstName" />

    @* When I use this, the Model does update *@
    <InputText @bind-Value="model.FirstName" />

    <p>fname: @model.FirstName</p>

    <input type="submit" />
</BfForm>

As mentioned in the comments, if I use RenderFragment<EditContext> in BfForm.razor, nothing gets rendered, instead I see Microsoft.AspNetCore.Components.RenderFragment``1[Microsoft.AspNetCore.Components.Forms.EditContext].

However, if I use RenderFragment, the inputs get rendered, but the Model doesn't get updated.

And in BfText.razor, I've tried using Blazor's InputText component with the @bind-Value attribute, an html input element with both @bind= and @bind-value and the Model does not get updated in any cases.

Finally, when using my BfForm component, if I use Blazor's InputText component, the Model does indeed get updated.

Not sure what I'm doing wrong here. Since I'm inheriting from InputBase<string> seems like my BfText component should be grabbing the EditForm's CascadingValue.

I've got the source code sitting in a class library on github.

For convenience, here's the source code for EditForm, InputBase, and InputText

Any help appreciated.

1

There are 1 best solutions below

1
On

Here's my attempt to simplify your code to help isolate the issue. This code works, so what are you doing that's different?

MyInput.razor

@inherits InputBase<string>

<input 
    type="text" @attributes="AdditionalAttributes" @bind-value="CurrentValue" />

@code {
    protected override bool TryParseValueFromString(string? value, out string? result, out string validationErrorMessage)
    {
        result = value;
        validationErrorMessage = string.Empty;
        return true;
    }
}

MyForm.razor

@using Microsoft.AspNetCore.Components.Forms

<EditForm Model="Model" OnInvalidSubmit="OnInvalidSubmit" OnValidSubmit="OnValidSubmit" novalidate>
    <DataAnnotationsValidator />
    <ValidationSummary />
    @ChildContent
</EditForm>

@code{
    [EditorRequired, Parameter] public RenderFragment? ChildContent { get; set; }
    [EditorRequired, Parameter] public Object? Model { get; set; }
    [Parameter] public EventCallback OnInvalidSubmit { get; set; }
    [Parameter] public EventCallback OnValidSubmit { get; set; }
}

Index.razor

@page "/"

<PageTitle>Index</PageTitle>

<h3>Index</h3>

<MyForm Model=model>
    <MyInputText @bind-Value=this.model.Value />
</MyForm>
<div class="p-2">
    Value: @model.Value
</div>

@code {
    private Model model = new();

    public class Model
    {
        public string? Value { get; set; }
    }
}