Tutorial Java Web: JSF + Hibernate + BootsFaces [Parte 8: Construyendo la Capa de Presentación]
Hola 👋 y bienvenido a la octava parte de este tutorial en el que construiremos nuestra propia aplicación de tareas en JSF. En el tutorial anterior vimos una introducción a los Managed Beans y cómo podemos usarlos para construir los controladores de nuestra aplicación incluso construimos dos vistas que se comunicaban cada una con su propio bean. El objeto de este tutorial es:
- Darte una pequeña introducción a las etiquetas propias de JSF
- Construir la capa de presentación, mediante la cual podamos crear los usuarios y las tareas a través de sus respectivas vistas
Advertencia 🚨: Tutorial largo adelante.
Para empezar, abre tu proyecto JSFTutorial en tu IDE Eclipse y dirígete a tu a la carpeta WebContent > pages, en el archivo usuarios.xhtml
reemplaza la línea:
<html xmlns="http://www.w3.org/1999/xhtml">
Por esta:
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html">
Lo que estamos haciendo es crear un nuevo espacio de nombre (namespace) bajo el alias de h que traerá consigo las etiquetas html de JSF. La mayoría de etiquetas html de JSF imitan a las etiquetas normales de HTML, con la excepción de que las primeras nos permitan hacer uso del Lenguaje de Expresión para comunicarnos con nuestros Managed Beans.
Este es nuestro código en el archivo usuarios.xhtml
actualmente con etiquetas HTML:
Este es el equivalente con etiquetas html JSF:
Donde:
<h:link value = "Usuarios" outcome = "usuarios />
Se renderiza como una etiqueta de ancla de la siguiente forma:
<a href = "./pages/usuarios.jsf">Usuarios</a>
Y además la etiqueta <h:outputText value="#{}"/>
renderiza una etiqueta de párrafo con el resultado de la evaluación de la expresión dentro del atributo value. Si reemplazas tu código con este notarás que no hay diferencia en el producto final.
Para nuestro propósito, necesitamos un form
con el que podamos recolectar los datos necesarios para crear un usuario y JSF puede asistirnos en esa tarea.
Creando un Form para los Usuarios
Primero remueve la etiqueta <h:outputText value=""/>
y en su lugar crea un form con las etiquetas <h:form>
tal que así:
A continuación vamos a colocar el equivalente a una etiqueta input utilizando la etiqueta <h:inputText id="" value=""/>
, colocando tantas como campos necesitemos llenar en nuestra tabla tal que así:
Una buena forma no está completa sin un botón que diga enviar, para ello vamos a agregar el equivalente a una etiqueta button, utilizando la etiqueta <h:commandButton id="" value=""/>
, tal que así:
Si ejecutas tu aplicación en el servidor, observaras que se muestran las respectivas cajas de texto junto con el botón de enviar, sin embargo y como es de esperarse, este botón no hace nada, eso es porque no hemos unido nuestro form
con nuestro Managed Bean.
Para comenzar, crea una nueva instancia de la entidad Usuario en tu clase UsuarioController (puedes borrar el código del tutorial anterior), piensa en ella como el recipiente el cual guardará la información que se ingrese a través del form
:
Luego en cada etiqueta <h:inputText />
reemplaza cada instancia de value por el campo correspondiente en la clase Usuario usando Lenguaje de Expresión, tal que así:
Similar a como pasaba con el mensaje de Hola Mundo en el tutorial anterior, lo que está haciendo JSF por debajo de cuerdas en el atributo value es reemplazar la sentencia #{usuarioController.usuario.nombre}
por #{usuarioController.usuario.setNombre(value)}
donde value es el texto que el usuario de la aplicación introduce.
Ahora debemos definir la lógica para la acción de guardar el usuario en la base de datos, para ello crea una función en tu clase UsuarioController llamada guardarUsuario(), en ella, sólo por probar, vamos a mostrar en consola lo que el usuario ingresó, tal que así:
- Aunque puedes usar
System.out.println()
te recomiendo hacer uso de la claseLogger
que vimos unos tutoriales atrás
Como último paso hay que decirle a nuestro <h:commandButton />
que hacer al momento de darle click a través del atributo action
tal que así:
Ejecuta tu aplicación en el servidor e introduce algunos datos de prueba:
Al darle al botón verás en tu consola mensajes parecidos a los siguientes:
¡Genial! La información se está pasando correctamente desde nuestra vista hasta el controlador, ahora lo único que tenemos que hacer es repetir aquello que hicimos en el tutorial número 6, crear una nueva instancia de nuestra clase UsuarioService y guardar nuestro Usuario, tal que así:
Ejecuta tu aplicación una vez más con los datos de prueba que prefieras e intenta guardar el usuario (recuerda tener encendido tu motor de SQL) si todo marcha bien deberías ver un mensaje de confirmación de la clase UsuarioRepository diciendo que el usuario se ha guardado de forma satisfactoria ¡Hurra 🎉!
Notarás que cuando das click al botón de guardar en el form
las cajas de texto quedan llenas, cuándo lo ideal sería que su contenido fuese eliminado de manera que le sea fácil al usuario ingresar nueva información. La razón de esto es porque los elementos input
están atados a los campos del objeto usuario en la clase UsuarioController ,que están poblados. Para solucionarlo simplemente crea una nueva instancia después de haber guardado la información, tal que así:
Mostrando los Registros
Lo único mejor que tener la habilidad de guardar un registro es poder mostrar los que están guardados, hasta ahora nuestra interfaz sólo permite el ingreso de la información, pero ¿Qué pasa si el usuario final necesita ver los usuarios que existen hasta el momento?
Por suerte no tenemos que hacer nada nuevo en nuestro back-end pues nosotros ya disponemos de un método que nos devuelve todos los usuarios guardados en la base de datos (el método getAll() de nuestro repositorio), sólo tenemos que pasar esa lista a nuestra vista. Pensemos un momento acerca de eso, la idea es mostrar la información de cada uno de los usuarios, la estructura a nivel de interfaz será la misma para todos, luego entonces parece lógico si recorro mi lista y voy llenando mi estructura con la información del usuario, pero, ¿Puedo colocar ciclos en mi vista? La respuesta es sí, JSF nos provee de herramientas de iteración que si bien no son iguales a las programáticas, tienen los mismos fundamentos.
Para ello, deberemos importar a nuestro proyecto etiquetas adicionales de JSF conocidas como facelets, estas etiquetas fueron hechas con el propósito de ayudarnos modelar los distintos componentes de nuestra página.
En particular nos interesa la etiqueta <ui:repeat>
que tiene la siguiente estructura:
<ui:repeat value="#{lista}" var="item" varStatus="status">
</ui:repeat>
Puedes pensar en ella como una combinación de un ciclo forEach y un ciclo for común, donde:
- El atributo
value
contiene el objeto iterable - El atributo
var
contiene la instancia del objeto actual, cuyo alcance es el mismo que el de la etiqueta - El atributo
varStatus
contiene la instancia de un objeto con información referente al estado de la iteración, tales como el número de ciclos realizados, el primer y último elemento de la lista, si se trata de una iteración par o impar, etc
Para implementarlo, agrega el namespace ui
a tu página de la siguiente forma:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets">
Agrega la etiqueta <ui:repeat>
en tu archivo usuarios.xhtml
debajo del form que creamos anteriormente. En este caso el valor del atributo value
es el valor de retorno del método getAll(), el valor del atributo var
puede contener el nombre que desees pero recuerda que tienes que referenciar ese mismo nombre dentro de la etiqueta.
Ahora adentro de la etiqueta, muestra la información del usuario usando las etiquetas <h:output>
que vimos anteriormente, tal que así:
Sí ahora ejecutamos nuestra aplicación, veremos que nos aparecen los usuarios que tengamos registrados en la base de datos, en mi caso:
Y si registras más usuarios, verás que irán apareciendo al final de lista conforme se crean.
¡Listo 🎉! Con esto concluimos la vista de los usuarios, vamos a hacer lo mismo para las tareas, esta vez un poco más rápido.
Creando un Form para las Tareas
En tu archivo tareas.xhtml
crea un form similar al que hicimos en la sección anterior, con una caja de texto para cada campo, con la diferencia de que para la descripción es más apropiado usar un textarea, recuerda crear una instancia de la entidad Tarea en tu clase TareaController, tal que así:
Vamos a crear una nueva instancia de la clase TareaService y el método guardarTarea() el cual referenciaremos desde nuestro <h:commandButton>
para crear una nueva tarea, tal que así:
Esto se ve bien, pero si recuerdas del tutorial número 6 “Construyendo la Capa de Servicios”, una tarea necesita tener un usuario anclado a ella, podemos implementar esto de varias maneras, una forma fácil sería:
- Crear un nuevo campo en la vista
tareas.xhtml
en el que pidamos el nombre del usuario nuevo de la tarea - Crear un método en la clase UsuarioRepository (y en la clase UsuarioService) que nos devuelva un usuario cuyo nombre concuerde con el que le pasamos como parámetro
- Pasar el usuario al método save() de la clase TareaService
A largo plazo esta solución puede no funcionar, debido a que los nombres de los usuarios pueden repetirse, por lo que el resultado de la búsqueda no sería único, causando todo tipo de problemas. Para nuestro proyecto sin embargo, funcionará bien.
En tu clase UsuarioRepository
crea un método llamado getByNombre(), con la siguiente firma:
public Usuario getByNombre(String nombre) {}
Para la consulta recuerda que tenemos que usar JPQL.
En SQL haríamos algo como esto:
SELECT * FROM users WHERE users.nombre = "<nombre>"
El equivalente en JPQL seria:
SELECT u FROM Usuario u WHERE u.nombre = :nombre
Sigue los mismos pasos que hicimos en el tutorial número 5.2 “Construyendo la Capa de Persistencia”, el resultado final debería ser algo como esto:
A continuación en tu vista tareas.xhtml
crea una nueva caja de texto donde pidas el nombre del usuario, y ya que la clase Tarea no tiene un campo específico para guardar el nombre (String) del usuario, tenemos dos opciones:
- Crear una variable String auxiliar en la clase TareaController
- Crear un nuevo atributo en la clase Tarea anotado con
@Transient
(Si no recuerdas lo que esta anotación hace te invito a leer nuevamente el tutorial número 4 “Construyendo las Entidades”)
Si escogemos la primera opción, el resultado sería:
A continuación crea una nueva instancia de la clase UsuarioService en la clase TareaController y recupera el usuario a partir de la variable auxiliar, luego guarda la tarea como vimos en la sección anterior, tal que así:
Ejecuta tu aplicación en el servidor y en la página de tareas introduce unos datos de prueba junto con el nombre de algún usuario que exista en la base de datos:
Si todo ha salido bien, verás los siguientes mensajes en tu consola:
Y si revisas tu base de datos verás que, efectivamente, la tarea se ha guardado con Margarita como la responsable de la tarea.
En lo que respecta a mostrar las tareas guardadas en la base de datos, el proceso es idéntico al de la sección anterior, por ello te animo a que lo intentes tu mismo 😉.
Aquí está el código completo de la clase UsuarioController:
El código de la clase TareaController:
El código del archivo usuarios.xhtml
Código del archivo tareas.xhtml
:
¡Felicidades 🥳🥳! Con esto hemos terminado la capa de presentación de nuestra aplicación y no sólo eso, sino que en este punto ya tenemos una aplicación web JSF totalmente funcional.
Lo que sigue para nosotros en esta serie de tutoriales es refinar un poco nuestra aplicación, la capa de presentación funciona, sí, pero seamos honestos, si tu le muestras esto a un potencial cliente lo primero que hará será poner esta cara 🙄, es por eso que en próximos tutoriales veremos cómo embellecer nuestra aplicación utilizando BootsFaces una tecnología hecha para construir interfaces gráficas en JSF. Muchas gracias por leer hasta aquí y espero verte en una próxima ocasión.
Si te gustó el tutorial y quieres apoyarme para seguir creando más contenido así puedes hacerme una donación aquí.
Tutorial anterior: https://medium.com/@devtony101/tutorial-java-web-jsf-hibernate-bootsfaces-355867ca767e
Tutorial siguiente: https://medium.com/@devtony101/tutorial-java-web-jsf-hibernate-bootsfaces-571752c3a1c0