Usando TinyMCE

Añadir a nuestras aplicaciones zonas de texto que permitan al usuario introducir texto enriquecido puede ser muy útil en algunas aplicaciones. En Internet existen gran catidad de herramientas para crear este tipo de campos, para formularios de contacto u otros usos. Algunas son gratis y otras de pago, algunas tienen más opciones para el usuario que otras, o son más fáciles o complejas de configurar, pero el objetivo es siempre el mismo.

En este artículo vamos a ver como crear una aplicación para enviar un formulario de contacto con un campo de texto enriquecido. No incluiremos el envío en sí, si no, solamente, como integrar un editor RTF en nuestra aplicación Angular (lo que ya es bastante). Para el envío del correo, podríamos usar cualquier librería adecusada, dependiendo de la tecnología de servicor que empleemos, pero hacer eso aquí nos distraería del objetivo de este artículo.

En este ejemplo vamos a emplear TinyMCE, todo un clásico con años de presencia en el mercado, del que hay una edición gratuíta y otra PRO de pago. Nosotros, claro, usaremos la que es gratis que, ya de por sí, da más que suficientes prestaciones.

INSTALANDO TinyMCE

Este es el primer paso. Para ver claro lo que vamos a hacer, esta vez no trabajaremos sobre nuestro ya conocido CRUD, sino sobre un proyecto nuevo que vamos a crear con:

ng new ang6-tinymce -p a6t --routing true

En realidad, para este proyecto no necesitamos el enrutamiento, dado lo limitado y específico que lo vamos a hacer, pero, por buenas costumbres, lo pondremos de todos modos.

A continuación, con la consola de VSC abierta en la carpeta raíz del proyecto, teclearemos:

npm install tinymce --save

Tras su instalación, debemos referenciarlo, como todo lo que instalamos que implica CSS y/o JavaScript. Solo que, esta vez, la tarea es más laboriosa, dada la gran cantidad de elementos que debemos referneciar en angular.json. Cuando descargues la aplicación, échale un vistazo. Como sabes, TinyMCE está formado por plugins que implementan las distintas funcionalidades (formatos de letra, color picker, subida de imágenes, etc). Aunque en este ejemplo sólo usaremos unos pocos de estos, los dejaremos todos referenciados, de modo que, si más adelante, deseamos añadir otro, no tengamos que preocuparnos de si está o no referenciado.

Además, debemos instalar el tema base que viene por defecto (el aspecto de TinyMCE), o cualquier otro tema, así como el idioma en que queremos que aparezcan los textos del esitor RTF (por defecto es inglés de Estados Unidos). Veamos como:

LA CARPETA assets

Este directorio está dentro de la ruta src de cualquier proyecto Angular. Hasta ahora no lo hemos empleado. Sin embargo, es sumamente útil, y aquí aprenderemos a referenciarlo y usarlo de modo que nos ahorre bastantes problemas. Lo primero que tenemos que hacer es buscar un directorio llamado skins, en donde se ha instalado nuestro TinyMCE, concretamente, dentro de node_modules/tinymce/. Cortaremos el directorio skins, y lo pegaremos dentro de src/assets/.

Ahora viene lo de preparar TinyMCE para ponerlo en español (o el idioma que deseemos). Dentro de src/assets crearemos un directorio llamado langs y, dentro de este, copiaremos los archivos de idiomas que podemos bajarnos de la página oficial de TinyMCE (https://www.tiny.cloud/download/language-packages/).

Más adelante, usaremos el directorio assets para otras cosas pero, por ahora, ya tenemos el tinyMCE correctamente instalado, listo para empezar a trabajar.

UN COMPONENTE ESPECÍFICO

El TinyMCE crea un objeto que, al ser aplicado a un área de texto, muestra un editor RFT en la pantalla. Lo que haremos cuando empleemos esta herramienta en una aplicación Angular es que crearemos ese objeto en un componente o servicio que implementaremos allí dónde lo necesitemos. En este ejemplo, vamos a crear un componente para el objeto TinyMCE. Empezaremos por crear un módulo dónde integrarlo. Como es un componente al que, en teoría, podríamos llamar desde caulquier parte de la aplicación, crearemos un módulo llamado globals, como ya hemos hecho en otras ocasiones para otros componentes globales. Dentro de este, crearemos un componente exportable al que llamaremos, simplemente, rtf. A la hora de crear el módulo, como aquí no vamos a usar barras de navegación, ni nada que implique una navegación entre vistas, no usaremos enrutador:

ng g m globals
ng g c globals/rtf --export

Con esto tenemos un componente llamado rftComponent. Es muy simple, ya que su misión es muy limitada. Dentro de la vista del componente tendremos la definición de un textarea, con un valor id variable. De este modo, se lo asignaremos desde donde llamemos al componente. El código de rtf.component.html queda, por tanto, así:

Como ves, más simple no puede ser. Lo más «complicado» (por decir algo) lo tenemos en la lógica, en rtf.component.ts:

Vamos a ver que es lo que hace el controlador. Lo primero son las importaciones, en las líneas de la 1 a la 10. Además de importar las clases e interfaces que necesitaremos, también importamos los archivos de environment que ya conocemos.

En las líneas 12, 13 y 14 declaramos las variables de jQuery (en este controlador no las usamos pero, en otra implementación, podríamos llegar a usarlas) y una variable para poder usar TinyMCE a la que hemos llamado tinymce.

En la línea 23 recibimos, mediante @Input(), el nombre que va a tener el textarea al que se aplique el objeto TyniMCE que estamos creando. Como ves, se usa la variable elementId, que coincide con la que hemos declarado en la vista.

En la línea 24 se crea un emisor (EventEmitter) para «sacar» el contenido del RTF a donde nos interese que, en este caso, será el componente «padre», es decir, el que llame a este.

En las líneas 34 a 55 configuramos las opciones del TiniMCE. Como ves, lo hacemos dentro del método ngOnAfterViewInit(). Las opciones de configuración serán las que consideremos en cada impementación. Puedes leer sobre ellas en la documentación oficial de la herramienta (https://www.tiny.cloud/docs/). Detallarlas aquí todas sería inviable.

Por último, debemos «limpiar» el componente cuando es destruido, como ves en las líneas 57, 58 y 59.

IMPLEMENTANDO EL COMPONENTE

Vamos a crear un formulario en el que vamos a implementar el componente. Esta vez, como nuestra aplicación sólo va a tener esto, lo vamos a hacer en el componente principal (AppComponent). El formulario tendrá dos campos de texto, para el nombre y el correo electrónico del usuario, y un textarea RTF que lograremos implementando RTF component. No nos detenderemos en los detalles más básicos del formulario, ya que todo eso ya lo conocemos, y sería repetir más de lo mismo. De hecho, parte del formulario está copiado y pegado del CRUD, y sólo sigeramente retocado para simplificarlo un poco. Lo que sí quiero recordarte es que, en app.module.ts debes importar GlobalsModule, para poder usar rtfComponent desde AppComponent. Me imagino que ya está más que familiarizado con esto, pero nunca está de más recordarlo.

En la vista de AppComponent (app.component.html, en el lugar donde desees ubicar tu editor de texto enriquecido, debes incluir la siguiente línea:

<a6t-rtf [elementId]="'miMensaje'" (editorKeyup)="ponerMensaje($event)"></a6t-rtf>

Observa que le pasas el id del campo a través de un binding con la propiedad elementId, de la que hacíamos un @Input() en rtf.component.ts. Además, hacemos un binding de eventos de modos que, cuando tenga lugar el evento editorKeyup se enlace con el @Output de rtf.component.ts, y llame al método ponerMensaje($event), que está en el controlador de este componente (app.component.ts). De este modo, lo que logramos es que, cuando rtfComponent emita el evento editorKeyup, lo «escuche» AppComponent, y actúe en consecuencia. Observa, en rtf.component.ts, que este evento «emite» el contenido del editor RTF cuando se produce un evento keyup, o change, o mousemove. Este contenido se «recoge» en app.component.html y es pasado al método ponerMensaje(), de app.component.ts.

Al margen de esto, el controlador de AppComponent no tiene nada que no conozcas ya de sobra. Todo lo que tiene lo hemos empleado ya hasta la saciedad. La única «novedad» que vas a ver en este proyecto es la siguiente. Contamos con dos scripts PHP. Uno de ellos está detinado a recibir las imágenes que se incluyan, en su caso, en el RTF y almacenarlas en el servidor. El otro es el que se lanza cuando se pulsa el botón de envío y que, como hemos comentado, enviaría el contenido el formulario a un correo de contacto. Ya hemos mencionado que esa funcionalidad no la incluimos en este artículo por no desviar la atención. Lo que si te darás cuenta es que los dos scripts están en un directorio llamado php que, a su vez, está dentro de assets. Esto difiere de la forma en que hemos trabajado hasta ahora, en que los scripts PHP estaban en un directorio al margen de la aplicación. La ventaja de hacerlo como lo estamos haciendo ahora, aunque al principio pueda asustar un poco, es que, al terminar, tenemos todo el proyecto en un solo directorio raíz, lo que lo hace más cómodo de gestionar al subirlo a un servidor remoto.

ATENCIÓN. Si en tu RTF incluyes la posibilidad de insertar imágenes en el contenido, usado el plugin image y la opción images_upload_url de TinyMCE (mira la documentación para entender como funcionan), esa operativa no te funcionará durante las pruebas de desarrollo. Sólo al final, cuando compiles la versión de distribución podrás insertar imágenes en tu RTF. Esto se debe a la forma en que TinyMCE se comunica con el servidor en el modo de desarrollo. En el modo de producción funciona perfectamente.

CONCLUYENDO

Como ves, se pueden incluir todo tipo de librerías externas en una aplicación Angular. A veces es más simple, y otras más complejo, dependiendo de la librería a emplear. En todo caso, lo fascinante es la potencia de Angular para construir todo lo que necesitemos. En el próximo artículo veremos algo que, estoy seguro, te gustará. De momento, esta aplicación puedes descargartela en este enlace.