Search code examples
c#.netblazorblazor-server-sidemudblazor

How to get the html text from Blazored.TextEditor on callback function?


Error when getting the text from Blazored.TextEditor component (See at the bottom).

Below is my dialog box that has a TextEditor component that I am using from Blazored.TextEditor.

<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;
    }
}

This is my component, where I pass the description as Body, and a callback function to update the text on submit click.

<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);
    }
}

This is the error that I get when submit is clicked, to my understanding this is raised because the submit button closes the dialog box, and the component does no longer exists. Is there any way on how to prevent this issue?

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)

Solution

  • After test I have got the conclusion that the MarkupString makes the rendering incorrect when you Invoke the body. The behavior is only when the invoking is pending long enough after websocket is reconnected in local environment, it got rendered correctly.

    The solution is rather easy after long-time research. Modify your code to this:

    <EditorContent>
        <span>@((MarkupString)Body)</span>
    </EditorContent>
    

    And it will work.

    enter image description here