Blazored / TextEditor

Rich text editor for Blazor applications - Uses Quill JS
MIT License
276 stars 58 forks source link

"i.parentNode is null" exception when using (MarkupString) in EditorContent #11

Closed SimonLeeCooper closed 4 years ago

SimonLeeCooper commented 4 years ago

Hello!

I'm trying to use the BlazoredTextEditor for HTML content. As a result, I'm using in the EditorContent something like : @((MarkupString)myString) And during saving I'm using : myString = await this.TextEditor.GetHTML();

The problem is that, after the content is edited, the first time I click on my button to save, everything does well but the second time I have this exception :

blazor.server.js

Error: There was an error applying batch 9.

System.InvalidOperationException: TypeError: i.parentNode is null
   at Microsoft.AspNetCore.Components.RenderTree.Renderer.InvokeRenderCompletedCallsAfterUpdateDisplayTask(Task updateDisplayTask, Int32[] updatedComponents)
Microsoft.AspNetCore.Components.Server.Circuits.CircuitHost: Error: Unhandled exception in circuit '9fnrpBAs9lMqcYFP44fHv0A8JrVw-KV4A6av2yf152M'.

System.AggregateException: One or more errors occurred. (TypeError: i.parentNode is null)
 ---> System.InvalidOperationException: TypeError: i.parentNode is null
   at Microsoft.AspNetCore.Components.RenderTree.Renderer.InvokeRenderCompletedCallsAfterUpdateDisplayTask(Task updateDisplayTask, Int32[] updatedComponents)

If I remove the (MarkupString) from the EditorContent I don't have the issue anymore. Do you have an idea why this occurs?

Here is my code :

<BlazoredTextEditor @ref="TextPrerequisites">
    <ToolbarContent>
        <select class="ql-header">
            <option selected=""></option>
            <option value="1"></option>
            <option value="2"></option>
            <option value="3"></option>
        </select>
        <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>
        </span>
    </ToolbarContent>
    <EditorContent>
        @((MarkupString)testCase.PreConditions)
    </EditorContent>
</BlazoredTextEditor>

<button @onclick="HandleValidSubmit">
    Submit
</button>

@code {
    [Parameter]
    public TestCase testCase { get; set; }

    BlazoredTextEditor TextPrerequisites;

    private async void HandleValidSubmit()
    {
        testCase.PreConditions = await this.TextPrerequisites.GetHTML();
    }
}

Thanks

ADefWebserver commented 4 years ago

Does the sample code provided in this GitHub repo work for you? The Get HTML and Set HTML buttons should work and provide an example.

SimonLeeCooper commented 4 years ago

Yes, the sample code is working for me. And if I use a load button (same as SetHTML of the sample) instead of using EditorContent, it also works. However it's not an option for me to require a press on a button to load the data. That why I used "EditorContent" to have the data displayed directly when the page is visited.

ADefWebserver commented 4 years ago

When the person first comes to the page it loads correctly, right? After the person performs 'some sort of action', why can't you call .LoadHTMLContent ?

SimonLeeCooper commented 4 years ago

Yes, everything loads correctly. And I don't really need to call .LoadHTMLContent as I don't need to update the content of the TextEditor from the code, only the web page user modifies the content. In my code above, the Submit button only get the HTML content of the TextEditor then save it in database.

It seems that I have the exception if I place the return value of .GetHTML in the variable I also use in <EditorContent>

ADefWebserver commented 4 years ago

You cannot change the contents of <EditorContent> without using .LoadHTMLContent

SimonLeeCooper commented 4 years ago

OK, I understood. I was changing the content when trying to save in the same variable. I changed the code and it's working.

Thank you

siatki commented 3 years ago

Hi, I have exactly the same scenario and problem as @SimonLeeCooper had, but I did not find a solution. I fill in the content initially with

@((MarkupString)Value)

You cannot change the contents of <EditorContent> without using .LoadHTMLContent

When to use the function LoadHTMLContent after a user has manually modified the content and system (OnClick) retrieved it with GetHTML and saved it to the database?

ADefWebserver commented 3 years ago

When to use the function LoadHTMLContent after a user has manually modified the content and system (OnClick) retrieved it with GetHTML and saved it to the database?

@siatki - If I understand your question correctly, "after a user has manually modified the content" you simply need to save those changes to your database. You don't need to call .LoadHTMLContent because all the content you would want is already sitting on the page.

ADefWebserver commented 2 years ago

This is also the solution to the following error:

Error: 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)

w3bStuff commented 8 months ago

This is also the solution to the following error:

Error: 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)

What is the solution? I'm actually getting this error when the EditorContent is set with @((MarkupString) body) AFTER saving the updated content to the DB (i.e. after executing the submit callback of my EditForm). It works fine on load.

If I replace the content of EditorContent simply with @body if works fine and there are no errors (load nor submit).

As you pointed out @ADefWebserver there is no need for me to call .LoadHTMLContent as the content has already been updated by the user (and is saved correctly BTW). The value of the variable body is also up to date but for some reason @((MarkupString) body) throws the error when the component re-renders.

Any idea how to get it working?

ADefWebserver commented 8 months ago

The value of the variable body is also up to date but for some reason when the component re-renders @((MarkupString) body) throws the error.

This is normal behavior when using Blazor Server. Blazor in .Net 8 has a lot of changes so I am glad we did not make a bunch of changes assuming things would always behave the same.

w3bStuff commented 8 months ago

Alright in short, for anybody finding this issue in the future, the exception is thrown when the same variable is used inside EditorContent AND is used to get the value from .GetHTML().

In my case, the code causing an issue was similar to:

<BlazoredTextEditor @ref="richEditor">
    <ToolbarContent>
        <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>
    </ToolbarContent>
    <EditorContent>
        @((MarkupString) body)
    </EditorContent>
</BlazoredTextEditor>

<button @onclick="HandleSubmit">
    Submit
</button>

@code {
    [Parameter] public string body { get; set; }
    BlazoredTextEditor richEditor;

    private async void HandleSubmit()
    {
        body = await this.richEditor.GetHTML();
        // save 'body' to the database
    }
}

As @SimonLeeCooper and @ADefWebserver suggested (sorry guys it took me a while to understand what you were saying), the same variable cannot be used! So, I changed the code to something like:

<BlazoredTextEditor @ref="richEditor">
    <ToolbarContent>
        <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>
    </ToolbarContent>
    <EditorContent>
        @((MarkupString) initialValue)
    </EditorContent>
</BlazoredTextEditor>

<button @onclick="HandleSubmit">
    Submit
</button>

@code {
    [Parameter] public string body { get; set; }    
    public string initialValue { get; set; }
    BlazoredTextEditor richEditor;

    protected override void OnInitialized()
    {
        initialValue = body;    
    }
    private async void HandleSubmit()
    {
        body = await this.richEditor.GetHTML();
        // save 'body' to the database
    }
}