tinymce / tinymce-blazor

Blazor integration
MIT License
45 stars 13 forks source link

TinyMCE init and the case of the disappearing content #56

Closed chrissmithptml closed 1 year ago

chrissmithptml commented 1 year ago

So, this one came as a surprise to me.

Created a barebones Blazor Server app, added the TinyMCE editor to a page, and then wired up its config to a javascript function.

Blazor code (can't get much simpler than that) <Editor JsConfSrc="editorConfig" />

Javascript config

window.editorConfig = {
    height: 500,
    toolbar: 'undo redo | bold italic',
    menubar: false,
    init_instance_callback: function (editor) {
        editor.setContent('<b>testy testy</b>');
    }
};

FYI, I've also tried

setup: function (editor) {
        editor.on('init', function (e) {
            editor.setContent('<p>This is content set via the init function</p>');
        });
    }

Simple as could be. Yet when I load up the page, I can see the content in the editor flash for an instant, then it vanishes without a trace.

If I add a button to the page (which I click after the editor is loaded) as so:

<button onclick="tinymce.activeEditor.setContent('<b>testy testy</b>')">
click to set some nonsense in the editor
</button>

The content stays on the screen.

I am assuming this is related to the init logic and some race condition whereby the editor is receiving the content I want set and then inexplicably clears it once the editor is fully initialised.

Browsers Attempted:

I can provide my ridiculously simple example Blazor app if need be.

exalate-issue-sync[bot] commented 1 year ago

Ref: INT-3032

chrissmithptml commented 1 year ago

Here's a public repo with example code.

https://github.com/chrissmithptml/TinyMceBlazorInitSad

jscasca commented 1 year ago

@chrissmithptml I haven't looked at the repo but definitely sounds like a race condition. My question is why are you trying to set the editor content on init? Why not use the Value property? Or use variable binding to edit the content instead of doing activeEditor.setContent.

For example:

<Editor @bind-Value="someVar" >
<Button @onclick=@"someVar='New content'" >
...
someVar = "Initial content"

Not sure about the syntax since I don't have an environment at hand

chrissmithptml commented 1 year ago

@jscasca good question.
That's what I have at present, but I noted a few performance issues with this, much of it related to Blazor Server / SignalR lag.

So I am building an app which is really a writer's tool, with each chapter of a book represented on the left and the editor on the right. So every time the user switches from chapter to chapter, the content in the editor is being updated with their chapter's html.

Now, when it comes to Blazor Server, with the use of SignalR as the bridge between the server and the web browser means

  1. I set the value of the editor when the page (aka the chapter they're editing) loads
  2. Blazor Server has to pass that down the wire via SignalR to re-render the client-side editor
  3. Lag lag lag

Depending on the size of the chapter they're editing, the lag can go from miniscule to irritating quite quickly. In order to remove this lag, I was able to prove that simply having the client-side javascript call an API endpoint via fetch to get the chapter contents and render them in the TinyMCE editor via the setContent(chapterContents) method was infinitely faster (almost no lag at all).

I bumped into this init issue in that the first render of the editor meant the chapter contents were NOT displaying but all subsequent clicks on other chapters and the contents rendered fine.

Thus how we got here :)

I am simply trying to make sure it's "not just me". Is the behaviour I'm seeing accurate?

chrissmithptml commented 1 year ago

As the issue is happening even when I use a purely Javascript solution

It seems to me this might go beyond Blazor and just be an underlying issue with the TinyMCE init method, but I could be wrong about that. Good to find out.

jscasca commented 1 year ago

The Blazor component will try to set the editor value after initialising. So what is happening is that the init function runs and after the component set the value to a blank editor.

In any case, I still recommend setting the Value property as the initial value (not binding). and work client side all your changes. E.g <Editor Value="your iniital Value" >

jscasca commented 1 year ago

Why not use the Blazor WebAssembly instead?

chrissmithptml commented 1 year ago

I'll see what I can work out using the Value attribute. I reckon I can trick it / detect if it's first load or not.

RE: WebAssembly. Too late, I'm afraid. We've dumped heaps of time into server-side, so no turning back. Just one of the caveats of signalr, I guess.

Cheers.

jscasca commented 1 year ago

Fair. If you look at the component code you'll see it hooks into the init event to set the initial value asynchronously. So that might be hard to hijack. You can probably hook once into the setcontent event to detect the first value setting or wait after the init event. Good luck!