Para editar el html de las entradas de este blog utilizo el control CkEditor


Publicado el viernes, 25 de marzo de 2016


CKEditor es un editor de texto HTML listo para usar, diseñado para simplificar la creación de contenidos web. Es un editor WYSIWYG que reúne las características de un procesador de palabras pero aplicado a una página web.

El CkEditor es el control principal de este blog para crear su contenido, lo uso en las pantallas de creación y edición de los Posts.

Voy a mostrar los primeros pasos que di creando el mantenimiento de posts y llegando hasta la utilización del control CkEditor para poder dar formato al texto. Empezaré utilizando Scaffolding para tener una base, posteriormente centralizaré la edición en un PartialView y finalmente sustituiré el control estándar de introducción de un texto por un CkEditor.

Antes de mostrar cómo utilizar control CkEditor en las pantallas de edición hay una serie de pasos previos que son necesarios para la creación del mantenimiento de Posts.

Aplicando Scaffolding a los Posts

Para tener una base de pantallas y controlador inicial en el mantenimiento de posts volví a aplicar Scaffolding pero esta vez con la entidad Post. Veamos el resultado.

Botón derecho sobre Controllers > Add... > New Scaffolded Item...

Seleccionar MVC5 Controller with views, using Entity Framework:

Como "Model Class" seleccionar la entidad Post de la capa Blog.Modelo y como "Data context class" ContextoBaseDatos, la clase que implementa DbContext de EF.

Como nombre para el controlador seleccioné PostsController. Su misión será "solamente" editar, crear y eliminar posts.

Los archivos que crea el Scaffolding son el PostsController y las cinco vistas Create.cshtml, Delete.cshtml, Details.cshtml, Edit.cshtml e Index.cshtml:

Para ver el resultado en el navegador ejecutamos la aplicación (F5) y navegamos a la dirección /posts:

La vista por defecto que se visualiza en esta ruta es Index.cshtml que contiene una lista de posts, vacía la primera vez que se accede.

Haciendo clic en el link "Create new" llegamos por fin a la pantalla de edición de un post:

En esta primera versión todos los campos de texto tienen un control "input" estándar. Este tipo de control "input" es suficiente para algunos campos, pero para el caso del campo "ContenidoHtml" un simple campo de texto no es suficiente. Necesitamos un control que permita introducir gran cantidad de texto y que además le pueda dar formato.

Un primer candidato que podría cumplir con estas especificaciones sería un control Textarea puesto que puede crecer a medida que introducimos texto, cosa que nos resuelve la introducción de una cantidad de texto extensa. Sin embargo quedaría cojo si queremos dar formato al texto. Es cierto que introduciendo html puro en el Textarea resolveríamos el problema, pero esta solución sería demasiado engorrosa. La solución más utilizada en estos casos son los controles especializados WYSIWYG (What You See Is What You Get). CkEditor es un control de este tipo y es por el que me he decantado a usar en este blog. Cabe tener en cuenta que no es el único control de este tipo, existen muchos más y cualquiera de ellos sería válido.

Añadiendo un Partial para centralizar la edición

El siguiente paso sería crear el control CkEditor y sustituirlo por los "inputs" estándar pero antes prefiero unificar las partes en común que tienen las vistas Create.cshtml y Edit.cshtml para no tener que repetir el código en las dos vistas. Si nos fijamos, la parte central de ambas vistas es idéntica

Parte común en el archivo Create.cshtml:

Parte común de Edit.cshtml

Para unificar esta parte común voy a crear un EditorTemplate (que es un tipo de PartialView) que compartirán ambas vistas. El EditorTemplate se llamará EditorPost y lo situaré en una carpeta denominada "EditorTemplates" dentro de Posts. Esto es una convención de MVC5 que me permitirá trabajar más cómodamente con el nuevo Editor.

Veámos como crearlo.

Crear la carpeta "EditorTemplates" y con el botón derecho hacer clic sobre ella > Add > View...

Indicamos un nombre, en este caso EditorPost y seleccionamos "Create as a partial view":

Este es el resultado en el "Solution Explorer"

El archivo está vacío así que añadimos el código común que hemos detectado. En la parte superior de EditorPost añado la cláusula @model Blog.Modelo.Post que indica que el modelo utilizado por la vista será el del Post.

Mantengo el @Html.HiddenFor(model => model.Id) que no existía en la vista Create.cshtml pero que no molesta y que para la edición es necesario.

Una vez creado el Editor podemos volver a la vista Create.cshtml y sustituir los controles por la llamada al helper que utiliza el editor creado:

La línea de código @Html.EditorFor(m => m, "EditorPost") primero busca un PartialView en la carpeta EditorTemplates de Posts, y si no la encuentra va a buscar dentro de la carpeta "Shared". En caso de que tampoco la encontrarse en esa carpeta entonces la aplicación mostraría un error.

Hacemos lo mismo en la vista Edit.html:

Con esto hemos conseguido centralizar la edición de las vistas. A partir de ahora, cambiar un "input" en el archivo EditorPost.cshtml tendrá efecto en las dos vistas.

Instalando CkEditor

Por fin llegamos a la parte más interesante, sustituir el input ContenidoHtml por un control que permita editar Html de manera cómoda. Para ello necesitamos tener el control CkEditor instalado. Por suerte podemos utilizar NuGet para instalarlo rápidamente:

Hacer clic con el botón derecho sobre "References" > "Manage NuGet Packages"

Seleccionar "Browse". Introducir en el buscador CkEditor, seleccionar el componente ckeditor-full que contiene una versión más actualizada y hacer clic en "Install":

El resultado es una nueva carpeta llamada "ckeditor" dentro de "Scripts".

Creando un control CkEditor

Una vez instalado ya lo tenemos todo a punto para crear el control.

Nos interesa que cualquier campo de texto formateable pueda utilizar un CkEditor y que lo tengamos centralizado en un solo archivo. Para conseguirlo podemos utilizar de nuevo el concepto de EditorTemplates. Crearé un EditorTemplate que podrá ser usado en cualquier vista.

Para ello, creo una PartialView dentro de la carpeta EditorTemplates de Views/Shared. 

El resultado en el Solution Explorer tras la creación:

Y el código del control es el siguiente:

El control tiene tres partes:

  1. La cláusula dónde se indica el modelo @model string
     
  2. El javascript que busca el control y lo sustituye por un CkEditor.
     
  3. El control Textarea que sirve como recipiente para el campo: @Html.TextArea(string.Empty, ViewData.TemplateInfo.FormattedModelValue)

El primer parámetro de @Html.TextArea es el nombre del control. Si se deja en blanco el sistema generará un nombre por defecto, en este caso el nombre del modelo. Por otro lado, utilizo el objeto ViewData.TemplateInfo que encapsula información sobre el contexto de la plantilla/template. En este caso utilizo la propiedad FormattedModelValue para obtener el valor del modelo. Cuando estemos en modo creación de posts este valor estará vacío, pero en el caso de la edición contendrá un texto.

Para identificar el ID del control en el javascript que sustituye el TextArea por el CkEditor utilizo @ViewData.TemplateInfo.GetFullHtmlFieldName(string.Empty) que recupera el ID completo de un campo del DOM.

El CkEditor permite establecer ciertas propiedades para configurarlo, en esta primera versión configuro solamente las propiedades filebrowserWindowWidth filebrowserWindowHeight para establecer el ancho y lo largo del control.

Una vez implementado el control, podemos ir al EditorPost.cshtml e indicar que queremos utilizar el editor de html para el campo "ContenidoHtml":

La clave es la línea:

@Html.EditorFor(model => model.ContenidoHtml, "HtmlEditor", new {htmlAttributes = new {@class = "form-control"}}) que contiene el nombre del EditorTemplate que se va a utilizar.

Por último hay que hacer un par de modificaciones en las vistas. Para empezar hay que añadir las referencias a los scripts de ckeditor en la dos vistas:

<script src="~/Scripts/ckeditor/ckeditor.js"></script>
<script src="~/Scripts/ckeditor/config.js"></script>

Ejemplo de la vista Create.cshtml (hay que hacer lo mismo en la vista Edit.cshtml)

Además en _layout.cshtml hay que subir el bundle de jquery a la parte superior de la página:

Esto es necesario porque utilizamos jQuery en el punto de entrada del javascript del control HtmlEditor.cshtml que hemos creado.

$(document).ready(function()

No podría ejecutarse esta línea si previamente no está cargada la librería de jQuery.

Con todo en su lugar, este es el resultado de cambiar el input de un campo de texto estándar por un ckeditor:

El texto que se va introduciendo se guarda internamente como Html puro. De este modo, creamos contenido web dinámico que se visualiza igual que si fuera cualquier página html. 

En la imagen anterior aparece el CkEditor con su configuración por defecto con todos los comandos existentes, en inglés y con un estilo monocromático, pero cualquiera de estos aspectos es configurable.

Se puede configurar "inline" cuando instanciamos "CKEDITOR.replace" tal como he hecho con las propiedades  filebrowserWindowWidth filebrowserWindowHeight o se puede centralizar su configuración en el archivo "config.js" que se encuentra dentro de la carpeta /Scripts/CkEditor.

Dado que el código del control es javascript y lo tenemos en nuestra solución, podríamos crear "plugins" a nuestro gusto o podríamos instalarlos de terceros puesto que ya existen muchos.

Finalizando

Los controles estándar html no sirven para crear contenido web, para ello se necesitan controles especializados. En este blog seleccioné CkEditor para este cometido pero existen multitud de controles alternativos válidos.

He mostrado cómo instalé el control con la ayuda de NuGet y cómo centralizarlo en un EditorTemplate reutilizable en cualquier vista.

Queda pendiente explicar en futuras entradas cómo configuré el control en castellano, qué comandos configuré para la edición de posts y el código que añadí para poder introducir imágenes en las entradas.