Usar un componente Blazor como contenido de un diálogo personalizado en TinyMCE

Este tutorial trata diversos temas avanzados de gestión de módulos y librerías javascript usando la API javascript de Blazor. Para el estudio de caso se ha elegido la librería TinyMCE y una aplicación específica: el uso de un componente Blazor para crear una ventana de diálogo personalizada en TinyMCE. Este tutorial constituye una buena base para la creación de un componente TinyMCE que usa una ventana de diálogo personalizada para gestionar una galería de imágenes, que pueden ser seleccionadas e insertadas en el editor TinyMCE. Otro aspecto avanzado constituye la gestión de configuraciones en TinyMCE a partir de un objeto C#, para permitir una configuración transparente desde ASP .NET sin necesidad de modificar ningún archivo javascript.

Crear una librería personalizada para TinyMCE con carga automática de archivos javascript

La idea detrás de este tutorial es la de mostrar como crear una librería Blazor para albergar un componente personalizado, asi como, una técnica que permita cargar los archivos javascript de forma automática. Para ello usaremos TinyMCE que es un editor de texto muy conocido y de grandes capacidades. Si bien, existe un componente oficial de TinyMCE para Blazor, el objetivo de este tutorial es el de mostrar unas técnicas que pueden ser útiles en otros contextos. Empecemos.

Puedes encontrar el código fuente de este tutorial en nuestro repositorio de GitHub.

Creación de un nuevo proyecto

En primer lugar vamos a crear  una nueva solucion y dos proyectos para albergar nuesto código. Para empezar abre Visual Studio 2022 y crea un nuevo proyecto de tipo Blazor Server App y pulsa en siguiente. 

blazor-server-app-project.png

Rellena las propiedades del nuevo proyecto. Dale un nombre al proyecto y una ubicación y pulsa siguiente.

Screenshot 2023-07-03 103454.png

En la siguiente ventana puedes dejar los parámetros por defecto y pulsar el botón crear.

Screenshot 2023-07-03 104227.png

El proyecto creará una nueva solución y un nuevo proyecto en la ubicación que elegiste. El proyecto que acabamos de crear nos servirá para probar la librería en la que crearemos el componente. Ahora vamos a crear el proyecto de librería. En el explorador de soluciones, pulsa con el botón derecho del ratón en la raíz del proyecto para abrir el menú contextual y pulsa Añadir > Nuevo proyecto....

Screenshot 2023-07-03 104357.png

Screenshot 2023-07-03 110910.png

A continuación, selecciona el tipo de proyecto Razor Class Library y pulsa Siguiente.

Screenshot 2023-07-03 111525.png

Dale un nombre al nuevo proyecto dejando la ubicación como está y pulsa Siguiente. En la ventana siguiente deja los valores por defecto y pulsa Crear.

Screenshot 2023-07-03 111621.png

El nuevo proyecto que acabamos de crear permite la creación de una librería de componentes y es el proyecto que usaremos para crear nuestro componente personalizado.


Descarga de TinyMCE

Nuestro componente personalizado usa el editor TinyMCE que está desarrollado en javascipt. Para tener el máximo control posible necesitamos obtener los archivos que componen el editor. Estos archivos los podemos obtener en la siguiente direccion: https://www.tiny.cloud/get-tiny/. Pulsa en Download TinyMCE SDK Now para descargar TinyMCE.

En Visual Studio, en el proyecto EditorPersonalizadoComponente, dentro de la carpeta wwwroot crea una nueva carpeta js.

Screenshot 2023-07-03 132341.png

Extrae el archivo comprimido de TinyMCE en un directorio, por ejemplo en Descargas. Abre la carpeta y recorre los directorios hasta que encuentres el directorio tinymce que contiene todos los archivos de TinyMCE. Copia esta carpeta y todos los archivos que contiene dentro a la carpeta wwwroot/js .

En Visual Studio puedes 'pegar' una carpeta en un directorio del Explorador de Soluciones como si se tratara del Explorador de Windows para hacer la copia.

Screenshot 2023-07-03 120139.png

Después de copiar los archivos, en el explorador de soluciones deberías tener algo similar a esta imagen:

Screenshot 2023-07-03 132441.png

Hemos instalado los archivos de TinyMCE dentro de la carpeta wwwroot, que es la carpeta que contiene los archivos que serán accesibles desde internet, del mismo modo que las imágenes y otros recursos de una página web, como las hojas de estilos en cascada (css).

Ten en cuenta que todos los archivos que se encuentren dentro de la carpeta wwwroot de una librería de Razor, son accesibles a través de la ruta ./_content/NombreLibreria. En nuestro caso, el archivo tinymce.min.js es accesible en la ruta ./content/EditorPersonalizadoComponente/js/tinymce/tinymce.min.js.


Creación del componente en la librería personalizada

Para empezar, vamos a hacer un poco de limpieza. En el proyecto EditorPersonalizadoComponente elimina los archivos siguientes:

Nuestro nuevo componente necesitará dos archivos nuevos: un archivo de componente Blazor y un archivo javascript para la inicialización del componente. Vamos a crear en primer lugar el archivo de componente. En el Explorador de Soluciones selecciona el nodo del proyecto EditorPersonalizadoComponente y haz clic derecho con el ratón sobre él. Selecciona la opción Añadir > Nuevo elemento.  Según como hayas configurado tu Visual Studio pueden mostrarse dos interfaces diferentes para la creación de un nuevo componente. Si se abre una ventana en la que te pide el nombre del archivo, esta es la nueva interfaz simplificada, escribe EditorDeTexto.razor para el nuevo componente, y pulsa el botón Añadir.

Screenshot 2023-07-03 134512.png

Si no, se abrirá la ventana clásica de creación de elementos. Selecciona el tipo Componente de Razor, como nombre pon EditorDeTexto.razor y pulsa el botón Añadir.

Screenshot 2023-07-03 134929.png

Ahora repite el proceso, pero esta vez para crear un archivo javascript dentro de la carpeta wwwroot/js al cual llamaremos editorDeTexto.js. Después de añadir los dos archivos el árbol del proyecto debería verse así:

Screenshot 2023-07-03 135845.png

Una vez creados los archivos, vamos a proceder a escribir el código. Abre el archivo EditorDeTexto.razor y escribe el código a continuación:

@inject IJSRuntime _jsruntime

<textarea id="editor"></textarea>

@code {
    private Lazy<Task<IJSObjectReference>> moduleTask;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        await _jsruntime.InvokeVoidAsync("import", "./js/tinymce/tinymce.min.js");
        moduleTask = new(() => _jsruntime.InvokeAsync<IJSObjectReference>("import", "./js/editorDeTexto.js").AsTask());

        var module = await moduleTask.Value;
        await module.InvokeVoidAsync("initEditor", "#editor", "/js/tinymce");
    }
}

En el código anterior lo que hacemos es definir un área de texto (textarea) que servirá para renderizar el editor TinyMCE. Dentro del método OnAfterRenderedAsync, realizamos dos importaciones: una directa, que es la importación de la librería TinyMCE como módulo (lin. 10). La segunda es la carga del módulo javascript de nuestro editor (lin 11), pero esta vez guardamos una referencia para poder acceder a sus métodos desde C# en todo momento (lin. 13 - 15). Finalmente, en la linea 15, llamamos al método initEditor y le pasamos dos parámetros. Como veremos a continuación, esta función se ocupa de inicializar el editor TinyMCE.

Pasemos ahora al archivo editorDeTexto.js. Escribe el siguiente código:

export function initEditor(selector, base_url) {

    tinymce.init({
        selector: selector,
        base_url: base_url,
        suffix: '.min'
    });
}

En el código anterior estamos realizando una configuración básica del editor TinyMCE. El selector, es el elemento dentro del HTML que se usará para renderizar el editor. El base_url se usa para definir la url de base donde se encuentran el resto de archivos de TinyMCE. Como estamos haciendo una carga dinámica de la librería, es necesario darle la url de base para que TinyMCE sea capaz de cargar el resto de archivos. El último parámetro, suffix, guarda relación con la carga de archivos también, si nuestra distribución de TinyMCE  es una versión minimizada (minimized), debemos definir el sufijo a min, para que TinyMCE pueda construir correctamente las rutas a los archivos. Para saber si la distribución que descargaste está minimizada, observa los nombres de los archivos. Si éstos contienen xxxxx.min.xxx es una version minimizada, si no, es una versión normal. Un ejemplo de archivo minimizado es tinymce.min.js y uno normal tinymce.js.

Por último, en el archivo _Imports.razor del proyecto EditorPersonalizadoComponente, añade la siguiente línea de código:

@using Microsoft.JSInterop

El archivo quedará así:

@using Microsoft.AspNetCore.Components.Web
@using Microsoft.JSInterop

El archivo _Imports.razor es un archivo de declaración de importaciones de librería global. Este se encuentra en el directorio raíz del proyecto, y todas las importaciones declaradas aquí son válidas para todos los componentes definidos en la raíz y todos los subdirectorios del proyecto.

Con todo esto ya tenemos la base para nuestro componente personalizado.


Usar nuestro componente personalizado

Una vez hemos creado el componente base, ya estamos listos para mostrarlo. Procedamos ahora a importar la librería en el proyecto EditorPersonalizado. Selecciona el nodo Dependencias del proyecto EditorPersonalizado, haz clic derecho y selecciona la opción Añadir referencia de proyecto. En la nueva ventana asegúrate que la pestaña Proyectos esté visible y marca la casilla correspondiente al proyecto EditorPersonalizadoComponente. Para finalizar pulsa el botón OK.

Screenshot 2023-07-03 143031.png

Esto añadirá una referencia al proyecto EditorPersonalizadoComponente de tal manera que podremos acceder a todas sus clases y componentes desde el proyecto EditorPersonalizado

Modifica el archivo Pages/Index.razor para que quede así:

@page "/"

<PageTitle>Index</PageTitle>

<h1>Editor de texto personalizado</h1>

<EditorDeTexto />

Como ves, gracias a la gestión automática de los módulos javascript no ha sido necesaria ninguna referencia a ninguna librería javascript en el proyecto Blazor. Una simple llamada al componente <EditorDeTexto /> es suficiente.

Modifica el archivo _Imports.razor y añade la siguiente línea para hacer referencia al nuevo componente:

@using EditorPersonalizadoComponente

El archivo quedará así:

@using System.Net.Http
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.Authorization
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using Microsoft.AspNetCore.Components.Web.Virtualization
@using Microsoft.JSInterop
@using EditorPersonalizado
@using EditorPersonalizado.Shared
@using EditorPersonalizadoComponente

Ya solo nos queda probar. Pulsa F5 para ejecutar el proyecto. Deberías ver algo similar a esto:

Screenshot 2023-07-03 144957.png


Resumen

En esta primera parte, hemos aprendido a crear un proyecto para albergar una librería de clases y componenes de Razor. Hemos creado un componente que sirve para envolver un editor TinyMCE y hemos visto una técnica para la carga de las dependencias javascript de manera transparente y automática. 

También hemos visto cómo podemos incrustar los archivos necesarios dentro de la librería para que estén disponibles de una forma sencilla para el proyecto consumidor. Como has visto ninguna referencia a la librería TinyMCE ha sido necesaria.

En el siguiente apartado crearemos un componente que usaremos como diálogo personalizado en TinyMCE. También desarrollaremos una técnica para configurar TinyMCE desde Blazor.

Uso de un componente Blazor en una ventana de diálogo TinyMCE

En esta segunda parte del tutorial vamos a crear un componente de Blazor que usaremos en TinyMCE como diálogo personalizado. También veremos un método para gestionar la configuración de TinyMCE desde el exterior del componente.


Configuración del componente TinyMCE

TinyMCE es un editor de texto complejo. Este tiene muchas opciones configurables, que deben ser provistas durante la creación del control. Para ello se debe pasar al constructor de TinyMCE un objeto con las propiedades y sus valores. Todo ello ocurre al nivel de javascript. Nosotros estamos usando TinyMCE incrustado en un componente Blazor, con lo que tendremos que configurar TinyMCE a través de Blazor y C#. 

Para empezar vamos a crear una nueva clase dentro del proyecto EditorPersonalizadoComponente. Haz clic derecho en la raíz del proyecto y selecciona Añadir > Nuevo elemento. En la ventana que se abre selecciona el tipo de elemento Clase, como nombre, la llamaremos ParametrosEditor.cs y pulsa Añadir para finalizar.

Screenshot 2023-07-04 104323.png

Después de crear la nueva clase el proyecto EditorPersonalizadoComponente debería quedar así:

Screenshot 2023-07-04 104347.png

Escribe el siguiente código en ParametrosEditor.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace EditorPersonalizadoComponente
{
    public class ParametrosEditor
    {
      public ParametrosEditor(string selector)
        {
            Selector = selector;
        }

        public string Selector { get; set; }

        [JsonPropertyName("external_plugins")]
        public Dictionary<string,string> ExternalPlugins { get; set; } = new Dictionary<string, string>();
    }
}

El código anterior define una serie de propiedades en C#. Estas propiedades las usaremos para configurar el editor TinyMCE. Lo siguiente es modificar el archivo wwwroot/editorDeTexto.js para adaptarlo de forma que pueda gestionar los parámetros. Escribe el siguiente código:

export function initEditor(configuration) {

    tinymce.init({
        selector: configuration.selector,
        base_url: "./_content/EditorPersonalizadoComponente/js/tinymce",
        suffix: ".min"
    });
}

Ahora adaptaremos el componente EditorDeTexto.razor para que acepte un parámetro definiendo el objeto de configuración. Modifica el código existente del siguiente modo:

@inject IJSRuntime _jsruntime

<textarea id="editor"></textarea>

@code {
    [Parameter] public ParametrosEditor Parametros { get; set; }

    private Lazy<Task<IJSObjectReference>> moduleTask;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        await _jsruntime.InvokeVoidAsync("import", "./_content/EditorPersonalizadoComponente/js/tinymce/tinymce.min.js");
        moduleTask = new(() => _jsruntime.InvokeAsync<IJSObjectReference>("import", "./_content/EditorPersonalizadoComponente/js/editorDeTexto.js").AsTask());

        var module = await moduleTask.Value;
        await module.InvokeVoidAsync("initEditor", Parametros);
    }
}

Por último solo nos queda modificar la página Index.razor en el proyecto EditorPersonalizado, en el que vamos a adaptar la llamada al componente TinyMCE para proporcionar los parámetros. Modifica el código como sigue:

@page "/"

<PageTitle>Index</PageTitle>

<h1>Editor de texto personalizado</h1>

<EditorDeTexto Parametros="_parametrosEditor" />

@code {
    private ParametrosEditor _parametrosEditor;

    protected override void OnInitialized()
    {
        _parametrosEditor = new ParametrosEditor("#editor");
    }
}

Pulsa F5 para ejecutar el proyecto. De hecho visualmente nada ha cambiado, pues sigues viendo lo mismo que en la sección anterior, pero sí, hemos cambiado el modo en el que configuramos el editor TinyMCE. Este es el resultado:

Screenshot 2023-07-03 144957.png


Uso de un componente de Blazor para diálogo en TinyMCE

La API de TinyMCE ofrece un ámplio abanico de posibilidades de personalización y extensión. Una de estas es la creación de ventanas de diálogo personalizadas. TinyMCE ofrece dos tipos: ventanas de diálogo definidas en TinyMCE o ventanas de diálogo que renderizan una URL. Dado que nosotros trabajamos con Blazor, según qué operaciones queramos realizar, las que renderizan una URL son más prácticas. Imagina por ejemplo un navegador de recursos (una galería de imágenes) es más sencilla de desarrollar desde Blazor que usando funciones para crear un formulario, eventos para leer y escribir en el servidor a través de una API, etc. Por ello, en este tutorial nos vamos a focalizar en la API para crear ventanas de diálogo que renderizan una URL en TinyMCE.

Lo primero de todo es añadir un nuevo componente al proyecto EditorPersonalizado. Haz clic derecho sobre la carpeta Shared. En el menu contextual selecciona Añadir > Componente de Razor.  En la ventana de opciones, pondremos el nombre DialogoEditorComponente.razor y pulsamos Añadir para terminar.

Screenshot 2023-07-04 123630.png

Escribe el siguiente código dentro de DialogoEditorComponente.razor:

<h3>Dialogo personalizado en Blazor para TinyMCE</h3>

@code {

}

Otro elemento necesario para renderizar nuestro nuevo componente correctamente, es la plantilla de base. Toda página de Blazor hereda una plantilla que usa para renderizar el contenido. En nuestro caso, como deseamos renderizar un componente sin ningún otro contenido, nos conviene crear una plantilla vacía para renderizar solamente nuestro componente. Haz clic derecho en la carpeta Shared, añade un nuevo componente de Razor y llámalo LightLayout.razor. Escribe el siguiente código:

@inherits LayoutComponentBase

@Body

Si comparas la nueva plantilla con la plantilla por defecto MainLayout.razor, podrás observar la diferencia. Nuestra plantilla solo define el marcador @Body, que sirve para insertar el contenido del componente que la usa. En MainLayout.razor hay más código que sirve para maquetar el contenido de una forma más precisa.

Ahora vamos a crear una página para renderizar el componente diálogo, de modo que sea accesible a través de una URL. Dentro de la carpeta Pages, crea un nuevo Componente de Razor y llámalo DialogoEditorPagina.razor. Escribe el siguiente código:

@page "/editor-dialog"
@layout LightLayout

<DialogoEditorComponente />

En el código anterior hemos definido una nueva página con la URL /editor-dialog que renderiza el componente que creamos anteriormente. Además le indicamos al compilador que debe usar la nueva plantilla que creamos, LightLayout.razor. De este modo nos aseguramos que en la página no habrá más que el componente, sin ningún otro contenido superfluo.

El siguiente paso consiste en crear un archivo javascript con el que definiremos un plugin para TinyMCE. En este plugin, definiremos las opciones que permitan renderizar nuestro componente diálogo en una ventana diálogo de TinyMCE. En el proyecto EditorPersonalizado, dentro de la carpeta wwwroot/js crea un archivo javascript llamado editorDialogoPlugin.js y escribe el siguiente código:

tinymce.PluginManager.add('editorDialogo', (editor, url) => {
    const openDialog = () => editor.windowManager.openUrl({
        title: 'Dialogo personalizado',
        url: '/editor-dialog'
    });

    editor.ui.registry.addButton('editorDialogo', {
        icon: 'sharpen',
        tooltip: "Este boton abre un componente de Blazor dentro de una ventana de dialogo de TinyMCE",
        onAction: () => {
            openDialog();
        }
    });
    
    return {
        getMetadata: () => ({
            name: 'Ventana de dialogo personalizada',
            url: 'https://ejemplo.ej'
        })
    };
});

En la definición del plugin usamos la API que TinyMCE pode a disposición para registrar un nuevo plugin. Del mismo modo definimos un botón para la barra de herramientas. La función getMetadata() puede ser usada por el plugin help para mostrar información acerca de los plugins en uso.

Para que TinyMCE pueda usar nuestro nuevo plugin, debemos modificar los parámetros de TinyMCE,  la función de inicialización del editor y la página donde usamos el componente de TinyMCE. Empecemos por modificar el archivo ParametrosEditor.cs del proyecto EditorPersonalizadoComponente y añade una nueva propiedad:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json.Serialization;
using System.Threading.Tasks;

namespace EditorPersonalizadoComponente
{
    public class ParametrosEditor
    {
        public ParametrosEditor(string selector)
        {
            Selector = selector;
        }

        public string Selector { get; set; }

        public string Toolbar { get; set; } = "undo redo | blocks | bold italic | alignleft aligncenter alignright alignjustify | outdent indent";

        [JsonPropertyName("external_plugins")]
        public Dictionary<string, string> ExternalPlugins { get; set; } = new Dictionary<string, string>();
    }
}

La propiedad que hemos añadido sirve para configurar la barra de herramientas del editor TinyMCE. El valor por defecto corresponde con la configuración por defecto de TinyMCE. Esta configuración nos sirve de base para configurar la barra de herramientas.

Continuemos ahora con el archivo editorDeTexto.js del proyecto EditorPersonalizadoComponente, el código debe quedar así:

export function initEditor(configuration) { 

    var defaultOpts = tinymce.defaultOptions; 

    defaultOpts.selector = configuration.selector;
    defaultOpts.base_url = "./_content/EditorPersonalizadoComponente/js/tinymce";
    defaultOpts.suffix = ".min";
    defaultOpts.toolbar = configuration.toolbar;

    var extPlugCnt = Object.keys(configuration.external_plugins).length;

    if (extPlugCnt > 0) {
        var plugins = {};

        for (let property in configuration.external_plugins) {
            plugins[property] = configuration.external_plugins[property];
        }

        defaultOpts.external_plugins = plugins;
    }

    tinymce.init(defaultOpts);
}

En esta nueva versión del inicializador de TinyMCE hemos cambiado la estrategia para definir los parámetros. Ahora recuperamos primero los valores por defecto de TinyMCE y modificamos los parámetros necesarios. De este modo podemos añadir una cantidad arbitraria de plugins externos. Una vez configuradas las opciones, las usamos para inicializar TinyMCE.

Por último solo nos queda modificar la página Index.razor en el proyecto EditorPersonalizado, en el que vamos a adaptar la llamada al componente TinyMCE para proporcionar los nuevos parámetros. Modifica el código como sigue:

@page "/"

<PageTitle>Index</PageTitle>

<h1>Editor de texto personalizado</h1>

<EditorDeTexto Parametros="_parametrosEditor" />

@code {
    private ParametrosEditor _parametrosEditor;

    protected override void OnInitialized()
    {
        _parametrosEditor = new ParametrosEditor("#editor");
        _parametrosEditor.Toolbar += " | editorDialogo";
        _parametrosEditor.ExternalPlugins.Add("editorDialogo", "/js/editorDialogoPlugin.js");
    }
}

Ya estamos listos. Pulsa F5 para ejecutar el proyecto. Ahora deberías ver algo así:

Screenshot 2023-07-04 140646.png

Si observas, en la barra de herramientas hay un nuevo botón. En la definición del plugin, cuando definimos el botón de la barra de herramientas, definimos un icono llamado sharpen, este, corresponde con un diamante. Nuestro nuevo botón tiene un icono de diamante, haz clic y podrás ver tu componente Blazor como contenido de una ventana de diálogo de TinyMCE.

Screenshot 2023-07-04 140705.png


Comunicación entre el componente Blazor y TinyMCE

Ahora que podemos usar un componente de Blazor en nuestro editor TinyMCE, necesitamos un mecanismo de comunicación entre ambos. Para ello haremos uso de la función window.parent.postMessage(). Esta función permite enviar mensajes dentro de la misma ventana. Estos mensajes pueden ser leídos por cualquier elemento dentro de la misma página. TinyMCE implementa un mecanismo que lee los mensajes y busca aquellos que tengan un formato convenido, que permite que podamos ejecutar acciones en el editor desde la ventana de diálogo. Esto es así porque, desde la ventana de diálogo no tenemos acceso a la instancia de TinyMCE, ya que se trata de una página externa y, el único elemento común es el elemento window de javascript.

Vamos a implementar el mecanismo de comunicación entre el componente Blazor y TinyMCE. Crea un nuevo archivo dentro del proyecto EditorPersonalizado, en la carpeta wwwroot/js llamado dialogoEditorComponente.js y escribe el siguiente código:

export function sendMessage(message) {
    window.parent.postMessage({
        mceAction: 'insertContent',
        content: message
    }, '*');

    window.parent.postMessage({
        mceAction: 'close'
    }, '*');
}

En el código anterior definimos una función que envía un mensaje por el bus interno de la ventana de javascript. El valor del mensaje es un objeto, definido por TinyMCE y que debe ser respetado para que sea tenido en cuenta. Las acciones están definidas en la API de TinyMCE. Por ejemplo insertContent añade el contenido pasado en content donde se encuentra el cursor en el editor. La acción close, cierra la ventana de diálogo.

Para que conozcas más acerca la API para ventanas de diálogo puede visitar la documentación oficial.

El siguiente paso consiste en modificar el componente en sí mismo para añadir un poco más de funcionalidad que permita probar el envío de mensajes. Modifica el código de DialogoEditorComponente.razor como sigue:

@inject IJSRuntime _jsruntime

<div class="mt-3 px-4">
    <h3>Dialogo personalizado en Blazor para TinyMCE</h3>

    <label for="message" class="form-label">Mensaje</label>
    <input type="text" id="message" class="form-control" @bind-value="_messageValue" />
    <button type="button" class="btn btn-primary mt-3" @onclick="OnSend">Enviar</button>
</div>

@code {
    private Lazy<Task<IJSObjectReference>> moduleTask;
    private string _messageValue = "";

    protected override void OnAfterRender(bool firstRender)
    {
        moduleTask = new(() => _jsruntime.InvokeAsync<IJSObjectReference>("import", "./js/dialogoEditorComponente.js").AsTask());
    }

    private async void OnSend()
    {
        var module = await moduleTask.Value;
        await module.InvokeVoidAsync("sendMessage", _messageValue);
    }
}

Por último, vamos a ajustar un poco el tamaño de la ventana del plugin ya que, se ve un poco grande. Modifica el archivo editorDialogoPlugin.js y añade las propiedades para el ancho y el largo:

tinymce.PluginManager.add('editorDialogo', (editor, url) => {
    const openDialog = () => editor.windowManager.openUrl({
        title: 'Dialogo personalizado',
        url: '/editor-dialog',
        width: 700,
        height: 250
    });

    editor.ui.registry.addButton('editorDialogo', {
        icon: 'sharpen',
        tooltip: "Este boton abre un componente de Blazor dentro de una ventana de dialogo de TinyMCE",
        onAction: () => {
            openDialog();
        }
    });
    
    return {
        getMetadata: () => ({
            name: 'Ventana de dialogo personalizada',
            url: 'https://ejemplo.ej'
        })
    };
});

Todo listo. Pulsa F5 para arrancar el proyecto y abre la ventana de diálogo. Ahora tiene un nuevo aspecto:

Screenshot 2023-07-04 145320.png

Prueba a escribir un mensaje, por ejemplo Hola TinyMCE y pulsa el botón enviar. La ventana de diálogo se cerrará automáticamente y el mensaje aparecerá en el editor:

Screenshot 2023-07-04 145608.png


Resumen

En esta segunda parte hemos aprendido a crear un componente de Blazor y a usarlo como base para construir una ventana de diálogo personalizada de TinyMCE. También hemos aprendido a gestionar de un modo diferente los parámetros de inicialización de TinyMCE desde Blazor. Finalmente hemos visto el mecanismo de comunicación entre el componente Blazor y el editor TinyMCE que permite ejecutar acciones en el editor desde el componente Blazor.