You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在回调中获取Blazored.TextEditor的HTML文本并解决报错

解决Blazored.TextEditor在MudDialog中调用UpdateBody时的组件销毁错误

问题场景

封装自定义TextEditor组件并嵌入MudDialog对话框内,点击对话框保存按钮触发Submit方法时,调用textEditor.UpdateBody()获取HTML文本抛出错误,推测原因是对话框提前关闭导致组件已被销毁。

错误信息

System.AggregateException: One or more errors occurred. (TypeError: Cannot read properties of null (reading 'removeChild'))
 ---> System.InvalidOperationException: TypeError: Cannot read properties of null (reading 'removeChild')
   at Microsoft.AspNetCore.Components.RenderTree.Renderer.InvokeRenderCompletedCallsAfterUpdateDisplayTask(Task updateDisplayTask, Int32[] updatedComponents)

相关代码

对话框代码

<MudDialog>
    <DialogContent>
        <MudForm Model="@Model" @ref="@_form" Validation="@(Validator.ValidateValue(Model))">
            <MudGrid>
                <MudItem xs="12">
                    <MudTextField Label="@L["Name"]" @bind-Value="Model.Name" For="@(() => Model.Name)"
                                  Required="true" RequiredError="@L["Name is required!"]">
                    </MudTextField>
                </MudItem>
                <MudItem xs="12">
                    <TextEditor @ref="textEditor" Body="@Model.Description" BodyChanged="@OnBodyChanged" />
                </MudItem>
            </MudGrid>
        </MudForm>
    </DialogContent>
    <DialogActions>
        <MudButton OnClick="Cancel">Cancel</MudButton>
        <MudButton OnClick="Submit">Save</MudLoadingButton>
    </DialogActions>
</MudDialog>

@code {
    [CascadingParameter]
    private MudDialogInstance MudDialog { get; set; }
    [EditorRequired]
    [Parameter]
    public UpdateItemCommand Model { get; set; }
    private MudForm? _form;
    private TextEditor textEditor;

    private async Task Submit()
    {
        await _form!.Validate().ConfigureAwait(false);
        await textEditor.UpdateBody();  // This line throws an error.
        if(!_form!.IsValid) { return; }

        var result = await Mediator.Send(Model);
        if(result.Succeeded)
        {
            MudDialog.Close(DialogResult.Ok(true));
            Notify(result.SuccessMessage);
        }
        else
        {
            Notify(result.ErrorMessage);
        }
    }

    private void Cancel()
    {
        MudDialog.Cancel();
    }

    private void OnBodyChanged(string body)
    {
        Model.Description = body;
    }
}

自定义TextEditor组件代码

<BlazoredTextEditor @ref="@Editor">
    <ToolbarContent>
        <span class="ql-formats">
            <select class="ql-font">
                <option selected=""></option>
                <option value="serif"></option>
                <option value="monospace"></option>
            </select>
            <select class="ql-size">
                <option value="small"></option>
                <option selected=""></option>
                <option value="large"></option>
                <option value="huge"></option>
            </select>
        </span>
        <span class="ql-formats">
            <button class="ql-bold"></button>
            <button class="ql-italic"></button>
            <button class="ql-underline"></button>
            <button class="ql-strike"></button>
        </span>
        <span class="ql-formats">
            <select class="ql-color"></select>
            <select class="ql-background"></select>
        </span>
        <span class="ql-formats">
            <button class="ql-list" value="ordered"></button>
            <button class="ql-list" value="bullet"></button>
            <button class="ql-indent" value="-1"></button>
            <button class="ql-indent" value="+1"></button>
            <select class="ql-align">
                <option selected=""></option>
                <option value="center"></option>
                <option value="right"></option>
                <option value="justify"></option>
            </select>
        </span>
        <span class="ql-formats">
            <button class="ql-link"></button>
        </span>
    </ToolbarContent>
    <EditorContent>
        @((MarkupString)Body)
    </EditorContent>
</BlazoredTextEditor>

@code
{
    BlazoredTextEditor Editor;

    [Parameter] public string Body { get; set; } = string.Empty;
    [Parameter] public EventCallback<string> BodyChanged { get; set; }

    public async Task UpdateBody()
    {
        Body = await Editor.GetHTML();
        await BodyChanged.InvokeAsync(Body);
    }
}

解决方案

1. 调整异步操作顺序与线程上下文

原代码中使用.ConfigureAwait(false)会脱离UI线程,可能导致组件状态更新与销毁的时序问题。修改Submit方法,确保所有操作在UI线程完成,且先完成编辑器内容获取再处理后续逻辑:

private async Task Submit()
{
    // 先执行表单验证,确保在UI线程处理
    await _form!.Validate();
    if (!_form!.IsValid) 
    {
        return;
    }

    // 确保编辑器内容获取完成后再发送请求
    await textEditor.UpdateBody();

    var result = await Mediator.Send(Model);
    if (result.Succeeded)
    {
        // 所有异步操作完成后再关闭对话框
        MudDialog.Close(DialogResult.Ok(true));
        Notify(result.SuccessMessage);
    }
    else
    {
        Notify(result.ErrorMessage);
    }
}

2. 在TextEditor组件中添加空值防护

在自定义TextEditor的UpdateBody方法中增加Editor实例的空值检查,避免调用已销毁组件的方法:

public async Task UpdateBody()
{
    if (Editor == null)
    {
        return;
    }
    Body = await Editor.GetHTML();
    await BodyChanged.InvokeAsync(Body);
}

3. 延迟对话框关闭(可选)

如果仍存在时序问题,可以在确认操作成功后延迟关闭对话框,确保UI状态更新完成:

private async Task Submit()
{
    await _form!.Validate();
    if (!_form!.IsValid) 
    {
        return;
    }

    await textEditor.UpdateBody();

    var result = await Mediator.Send(Model);
    if (result.Succeeded)
    {
        Notify(result.SuccessMessage);
        // 延迟100ms确保UI更新完成后再关闭
        await Task.Delay(100);
        MudDialog.Close(DialogResult.Ok(true));
    }
    else
    {
        Notify(result.ErrorMessage);
    }
}

内容的提问来源于stack exchange,提问作者Sachihiro

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 10:15:54