Por 9.99€ al mes tendrás acceso completo a todos los cursos. Sin matrícula ni permanencia.
Características
Implementa el controlador y me da una ayuda con la vista. Conceptualmente struts2 y jsf hacen lo mismo. Hay que tener cuidado al meter código javascript en un documento con etiquetas JSF, se mezclan mal.
El código html y javascript generado por jsf:
- es difícil de entender. El generado por struts2, no.
- no debe ser modificado. Esto implica problemas cuando queremos crear o modificar tags de JSF.
Por otra parte, JSF es un estandar de java, de hecho utiliza las librerías JSTL.
Existen varias implementaciones de jsf:
MyFaces
RichFaces (quizás la más utilizada)
IceFaces
…
Cada versión implementa el standard, además te da sus propios tags.
El autor de Struts1 hizo JSF 1.
Hola mundo – redireccionamiento directo
index.html<meta http-equiv="Refresh" content= "0; URL=holaMundo.faces"/>
web.xml
En jsf todo lo que está mapeado con faces se redirige al servlet faces.
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.faces</url-pattern>
</servlet-mapping>
faces-config.xml
<?xml version="1.0"?>
<faces-config xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-facesconfig_1_2.xsd"
version="1.2">
</faces-config>
holaMundo.jsp
hola Mundo!
Una redirección a holaMundo.faces irá a holaMundo.jsp cargando el context de JSF (FacesContext). A partir de ese momento será posible utilizar las etiquetas de JSF.Falta enlace a projecto jsf: a_holaMundo.zip
Saludar – el action es un parámetro de navegación
saludar.jsp
<html>
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h"%>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f"%>
Las etiquetas de JSF deben estar dentro de las etiquetas <f:view>, las cuales sulen comenzar inmediatamente después de <body> y terminar inmediatamente antes de </body>
<f:view>
<h:form>
<h:outputText value="#{msgs.inicioSaludo}" />
<h:inputText value="#{SaludarBB.nombre}" />
Las etiquetas de JSF que utilicen el atributo action deben ir dentro de la etiqueta <h:form>
<h:commandButton action="next" />
</h:form>
</f:view>
</html>
messages.properties
inicioSaludo =hola, introduce tu nombre
hola= hola
web.xml
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.faces</url-pattern>
</servlet-mapping>
faces-config.xml
<navigation-rule>
<from-view-id>/saludar.jsp</from-view-id>
<navigation-case>
Tendremos un solo <from-view-id> por cada <navigation-rule>
<from-outcome>next</from-outcome>
<to-view-id>/pagina2.jsp</to-view-id>
</navigation-case>
</navigation-rule>
<managed-bean>
<managed-bean-name>SaludarBB</managed-bean-name>
<managed-bean-class>com.pablomonteserin.beans.SaludarBB</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
</managed-bean>
<application>
<resource-bundle>
Esto referencia al fichero messages.properties colocado en com.pablomonteserin
<base-name>com.pablomonteserin.messages</base-name>
<var>msgs</var>
</resource-bundle>
</application>
SaludarBB.java
package com.pablomonteserin.beans;
public class SaludarBB {
private String nombre;
public String getNombre() {
return nombre;
}
public void setNombre(String nombre) {
this.nombre = nombre;
}
}
<f:view>
<h:outputText value="#{msgs.hola}" />
<h:outputText value="#{SaludarBB.nombre}" />
</f:view>
Saludar – el action es un método del BackingBean
index.jsp
<html>
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h"%>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f"%>
<f:view>
<h:form>
<h:outputText value="#{msgs.inicioSaludo}" />
<h:inputText value="#{SaludarBB.nombre}" />
<h:commandButton action="#{SaludarBB.saludar}"/>
</h:form>
<h:outputText value="#{msgs.hola}" />
<h:outputText value="#{SaludarBB.nombre}" />
</f:view>
</html>
messages.properties
inicioSaludo = hola, introduce tu nombre
hola= hola
web.xml
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.faces</url-pattern>
</servlet-mapping>
faces-config.xml
<navigation-rule>
<from-view-id>/saludar.jsp</from-view-id>
<navigation-case>
<from-outcome>next</from-outcome>
<to-view-id>/pagina2.jsp</to-view-id>
</navigation-case>
</navigation-rule>
<managed-bean>
<managed-bean-name>SaludarBB</managed-bean-name>
<managed-bean-class>com.pablomonteserin.beans.SaludarBB</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
</managed-bean>
<application>
<resource-bundle>
<base-name>com.pablomonteserin.messages</base-name>
<var>msgs</var>
</resource-bundle>
</application>
SaludarBB.java
package com.pablomonteserin.beans;
public class SaludarBB {
private String nombre;
public String getNombre() {
return nombre;
}
public void setNombre(String nombre) {
this.nombre = nombre;
}
public String saludar(){
//busqueda en BD, etc...
/*devolvemos el parámetro de navegación,
equivalente a SUCCESS de struts */
return "next";
}
}
Saludar con parámetros
saludar.jsp
...
<h:outputFormat value="Hola, que pasa {0}">
<f:param value="#{SaludarBB.nombre}" />
</h:outputFormat>
<h:outputFormat value="#{msgs.hola}">
<f:param value="#{SaludarBB.nombre}" />
</h:outputFormat>
messages.properties
hola=Hola {0}
Ejercicio – sumador
Hacer una aplicación que sume dos números:
Cargar una Combo
Podemos poner explícitamente las option de la combo…
<h:selectOneMenu value="#{OperaBB.op}">
<f:selectItem itemLabel="suma" itemValue="suma" />
<f:selectItem itemLabel="resta" itemValue="resta" />
<f:selectItem itemLabel="multiplicacion" itemValue="multiplicacion" />
<f:selectItem itemLabel="division" itemValue="division" />
</h:selectOneMenu>
… o recuperar las combos del Backing Bean:
//Estas operaciones las recuperaríamos de la base de datos
//Para este método no es necesario declarar su correspondiente atributo, ya que sólo vamos a querer recuperarlo, no modificarlo-
//De esta forma, nos evitamos tener cargado en memoria grandes colecciones de datos.
<h:selectOneMenu value="#{OperaBB.op}">
<f:selectItems value="#{OperaBB.operaciones}"/>
</h:selectOneMenu>
public ArrayList<SelectItem> getOperaciones(){
ArrayList<SelectItem> al = new ArrayList<SelectItem>();
al.add(new SelectItem("suma", "+"));
al.add(new SelectItem("resta", "-"));
al.add(new SelectItem("multiplicacion", "*"));
al.add(new SelectItem("division", "/"));
return al;
}
Error típico
Objetivo inalcanzable, ‘objeto’ devolvió nulo: javax.el.PropertyNotFoundException
Comprobar: que el BackingBean al que estamos llamando:
- implementa la interfaz Serializable.
- tiene getters y setters de la propiedad que estamos recuperando.
- inicializa en su constructor vacío el objeto al que estamos llamando (en caso de que sea un objeto tipo Persona, con propiedades, etc. )
MantenimientoLibroBB(){ libro = new Libro(); ... }
- En el output text se está llamando a un método del BackingBean en lugar de imprimir el resultado.
Manejo de la sesión y el contexto
En el listener:
HttpSession session = arg0.getSession();
session.setAttribute("sumaTotal", new Integer(0));
ServletContext sc = arg0.getServletContext();
sc.setAttribute("sumaApplication", new Integer(0));
En el bean:
FacesContext context = FacesContext.getCurrentInstance();
Map sessionMap = (Map) context.getExternalContext().getSessionMap();
Map applicationMap = (Map) context.getExternalContext().getApplicationMap();
Imprimir valores en la vista, recogiendolos de la sesión:
<h:outputText value="#{sessionScope.sumaSesion}" />
<h:outputText value="#{applicationScope.sumaApplication}" />
Ejercicio
Hacer una página que me permita ingresar dos números y me muestre:
- El total de la suma.
- El total de la suma de los números en sesión.
- El total de la suma de todas las sumas del contexto.
Componente datatable de JSF
<h:dataTable value="#{LibroBB.libros}" var="libro"
columnClasses="column1,column2">
<h:column>
<f:facet name="header">
<h:outputText value="precio" />
</f:facet>
<h:outputText value="#{libro.precio}" />
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="titulo" />
</f:facet>
<h:outputText value="#{libro.titulo}" />
</h:column>
</h:dataTable>
Notas:
No poner <h:form> dentro de un datatable
<h:commandLink /> que mandan información a un BackingBean cuyo <managed-bean-scope> es request, no envían la información. Podría ser session.
Enlaces en JSF
Esta la etiqueta de JSF para hacer enlaces:
<h:outputLink id="link1" value="https://www.pablomonteserin.com">
texto enlace
</h:outputLink>
<h:commandLink value="Volver a inicio" action="inicio" />
<h:commandLink value="#{libro.id}" action="#{LibroBB.inicioModifica}">
<f:param value="#{libro.id}" name="libro.id" />
</h:commandLink>
Recuperar un parámetro en JSF no es tan trivial como en Struts 2. Para hacerlo, habrá que utilizar el siguiente código:
String idString = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("nombreDelParametro");
Ejercicio – librería
Una combo actualiza el resto de campos del formulario
modificacion.jsp<h:form id="formulario">
<h:selectOneMenu value="#{MantenimientoPacienteBB.paciente.id}" onchange="submit()" immediate="true" valueChangeListener="#{MantenimientoPacienteBB.consultaPaciente}">
<f:selectItems value="#{MantenimientoPacienteBB.selectItemPacientes}" />
</h:selectOneMenu><br/>
<h:outputText value="Introduzca el nombre" />
<!-- inmediate="true" permite completar una petición sin que el formulario enviado sea validado, enviando sólo los datos enviados-->
<h:inputText id="nombre" value="#{MantenimientoPacienteBB.paciente.nombre}" immediate="true" />
MantenimientoPacienteBB.java
public void consultaPaciente(ValueChangeEvent e){
Integer id = (Integer) e.getNewValue();
paciente=PacienteBO.getPaciente(id);
//esto para refrescar campos inputtext
HtmlInputText inputTextGP = (HtmlInputText)FacesContext.getCurrentInstance().getViewRoot().findComponent("formulario:nombre");
inputTextGP.setValue(paciente.getNombre());
FacesContext.getCurrentInstance().renderResponse();
}
Ejercicio – hospital
La base de datos tendrá 4 campos: id(PRIMARY KEY, AUTOINCREMENT), nombre (VARCHAR), apellidos (VARCHAR), fecha_alta(DATE).
Para convertir una fecha en un objeto de tipo Date:
SimpleDateFormat formatter = new SimpleDateFormat('dd-MM-yyyy');
Date fecha = formatter.parse(stringFecha'));
Estas conversiones las haremos en el controlador (No modificaremos el pojo paciente para añadirle las propiedades día, mes y año)