封装Blazor EditForm后自定义InputBase组件无法更新Model
Blazor自定义EditForm包装组件下自定义输入组件无法更新Model问题排查
问题现象
封装Blazor原生EditForm组件扩展自定义功能,同时开发继承自InputBase<T>的自定义输入组件时,出现以下异常:
- 原生
EditForm中使用自定义输入组件时,绑定的Model可正常更新 - 封装后的EditForm包装组件中使用自定义输入组件时,
Model无法被更新 - 封装后的EditForm包装组件中使用Blazor原生
InputText组件时,Model可正常更新
复现代码
1. 表单包装组件 BfForm.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; } // 切换为该类型时直接渲染会输出类型的ToString()结果 //[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; } }
2. 自定义文本输入组件 BfText.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 = null; return true; } }
3. 组件使用示例
<BfForm Model="model" OnInvalidSubmit="invalidSubmit" OnValidSubmit="validSubmit"> <BfText DisplayName="First name" @bind-Value="model.FirstName" /> @* 原生InputText可正常更新Model *@ <InputText @bind-Value="model.FirstName" /> <p>fname: @model.FirstName</p> <input type="submit" /> </BfForm>
额外测试结论
- BfForm中将
ChildContent声明为RenderFragment<EditContext>类型时,子内容无法正常渲染,页面仅显示该类型的ToString()结果:Microsoft.AspNetCore.Components.RenderFragment1[Microsoft.AspNetCore.Components.Forms.EditContext]` - BfForm中将
ChildContent声明为无参RenderFragment类型时,子内容可正常渲染、表单验证功能正常,但自定义输入组件无法更新Model - BfText组件测试过多种绑定实现:嵌套原生InputText做
@bind-Value绑定、直接使用html input元素测试@bind/@bind-value写法,在BfForm中均无法实现Model更新;但原生InputText组件在BfForm中可正常更新Model
根因分析
问题出在BfForm组件对ChildContent的类型声明和渲染逻辑上:
原生EditForm的ChildContent是RenderFragment<EditContext>类型,会将自身创建的EditContext实例作为参数传给子内容,供继承InputBase<T>的输入组件正确解析ValueExpression、生成FieldIdentifier、关联值变更通知链路。
之前的两种写法都存在问题:
- 声明为无参
RenderFragment:子内容中的自定义输入组件虽然能通过级联参数拿到EditContext,但ValueExpression的绑定上下文链路被截断,无法正确关联外层Model的属性,值变更后无法触发Model更新;原生InputText因为内部做了兼容处理所以不受影响。 - 声明为
RenderFragment<EditContext>但直接写@ChildContent渲染:没有给泛型RenderFragment传入必需的EditContext参数,Razor引擎会直接将RenderFragment委托对象作为普通内容渲染,因此会输出类型的ToString()结果。
修复方案
修改BfForm.razor,和原生EditForm保持一致使用RenderFragment<EditContext>类型声明ChildContent,渲染时传入EditForm提供的上下文参数:
@using Microsoft.AspNetCore.Components.Forms <EditForm Model="Model" OnInvalidSubmit="OnInvalidSubmit" OnValidSubmit="OnValidSubmit" novalidate> <DataAnnotationsValidator /> <ValidationSummary /> @* 传入EditContext上下文给子内容 *@ @ChildContent(context) </EditForm> @code{ [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; } }
修改后自定义输入组件即可正常更新Model,表单验证、提交逻辑均不受影响。
内容的提问来源于stack exchange,提问作者MikeT
相关产品推荐
相关产品推荐

