tinymce / tinymce-angular

Official TinyMCE Angular Component
MIT License
328 stars 92 forks source link

editor.ui.registry.addButton doesn't work in Angular app when changing pages #244

Closed IonelLupu closed 3 years ago

IonelLupu commented 3 years ago

What is the current behavior? Describe the bug If you have a TinyMCE editor on a page where you've added a button using editor.ui.registry.addButton then change to another page and then go back to the first page, the custom button doesn't show up anymore. The button is there after page refresh but not after Angular page transition.

Please provide the steps to reproduce and if possible a minimal demo of the problem via fiddle.tiny.cloud or similar.

Link to repo (hard to reproduce in a cloud environment)

Steps to reproduce:

  1. Add a self-hosted TinyMCE editor on a page inside your angular app.
    
    @NgModule({
    declarations: [
        AppComponent,
        Page1Component,
        Page2Component,
    ],
    imports: [
        EditorModule,
        BrowserModule,
        AppRoutingModule,
    ],
    providers: [
        {provide: TINYMCE_SCRIPT_SRC, useValue: 'tinymce/tinymce.min.js'}
    ],
    bootstrap: [AppComponent]
    })
    export class AppModule {
    constructor() {
        tinymce.defaultSettings = {
            menubar: false,
            height: 300,
            suffix: '.min',
            base_url: '/tinymce'
        }
    }}
2. Register a custom button for the editor:
```ts
import { AfterViewInit, Component, ViewChild } from '@angular/core'
import { EditorComponent } from '@tinymce/tinymce-angular'

export const TinyMCEConfig = {
    content_css: '/assets/tinymce.css',
    height: '100%',
    toolbar: `
        undo redo preview fullscreen |
        test |
    `
}

@Component({
    selector: 'app-page1',
    templateUrl: './page1.component.html',
    styleUrls: ['./page1.component.scss']
})
export class Page1Component implements AfterViewInit {
    editorConfig = TinyMCEConfig

    @ViewChild('editor') editorComponent!: EditorComponent

    ngAfterViewInit(): void {
        this.editorComponent.editor.ui.registry.addButton('test', {
            text: 'My Test button',
            onAction: () => {}
        })
    }
}
  1. Set the config for your TinyMCE editor to include the custom button
    <editor [init]="editorConfig" #editor style="height: 400px;"></editor>
  2. Use the Angular router to create a second page (the editor will be on the first page). The second page can be an empty component. The point is to change from Page 1 to Page 2. When doing that, the custom button will disappear.

What is the expected behavior? The expected behavior is to show the custom buttons even if you change pages in the Angular app.

Which versions of TinyMCE, and which browser / OS are affected by this issue? Did this work in previous versions of TinyMCE? This happens in every browser on version "tinymce": "^5.7.1" and "@tinymce/tinymce-angular": "^4.2.2". I didn't check older versions.

exalate-issue-sync[bot] commented 3 years ago

Ref: INT-2538

IonelLupu commented 3 years ago

@jscasca @techtangents any idea of a quick workaround this?

jscasca commented 3 years ago

Hey @IonelLupu my best guess is that there is a race condition going on in there somewhere. Can you try adding the button during the setup phase of the editor?

editorConfig: any = {...TinyMCEConfig, setup: (e) => e.ui.registry.addButton('test', {text: 'button', onAction: () => {}})};

Just nicer. I was just running a quick test in your repo

IonelLupu commented 3 years ago

hey @jscasca

Yes, this works. Thank you very much! 😄

Do you think we can track down this race condition or do you have any idea why did it happen?

tiny-james commented 3 years ago

It's because the editor is loaded by an asynchronous process so it is not immediately available on the this.editorComponent.editor variable after creation of the Angular integration. By registering the button on the setup callback we know for certain that the editor is available.

IonelLupu commented 3 years ago

The weird thing is that I tried to add a setTimeout in ngAfterViewInit and it didn't add the button at all anymore:

ngAfterViewInit(): void {
        setTimeout(() => {
            this.editorComponent.editor.ui.registry.addButton('test', {
                text: 'My Test button',
                onAction: () => {}
            })
        }, 2000)
    }

Is this how addButton() supposed to be used? Maybe I am not using it correctly.

Nonetheless, It works great with the setup approach.

jscasca commented 3 years ago

@IonelLupu To the best of my knowledge the buttons have to be added once the editor is available and before it has finished initialising. The race condition happens because the editor is asynchronously initialising and the ngAfterViewInit of your code fires before the editor has finished but if you wait longer the setup phase finishes before adding the button and nothing happens since it doesn't lallow buttons to be added dynamically in the toolbar. I'm not sure if this is actually documented somewhere but you can always ask in the original TinyMCE repo

I will close the ticket with the suggestion to add buttons during setup for best results :)