jsf - plantillas - Creación de páginas de detalles maestros para las entidades, cómo vincularlas y qué ámbito de bean elegir
managed bean concepto (2)
Empecé a aprender JSF, pero, lamentablemente, la mayoría de los tutoriales solo presentan un inicio de sesión o una sección de registro.
¿Puede indicarme algunos ejemplos más a fondo? Una cosa que me interesa es una página que presenta una lista de productos . Estoy en la página de inicio y presiono en los productos de la página para poder ver los últimos productos agregados. Y cada vez que visito la página, la lista de productos se creará a partir de las últimas entradas en la base de datos. ¿Cómo puedo manejar esto?
Una forma de resolver esto sería crear un bean administrado con ámbito de sesión en el que colocaría diferentes entidades actualizadas a través de otros beans administrados. Encontré este tipo de enfoque en algunos tutoriales, pero parece bastante difícil y torpe.
¿Cuál sería el mejor enfoque para resolver algo como esto? ¿Cuál es el uso correcto del alcance de la sesión en la interfaz de usuario maestra de detalles de dos páginas?
¿Cuál es el uso correcto del alcance de la sesión?
Úselo solo para los datos del ámbito de la sesión, nada más. Por ejemplo, el usuario que inició sesión, su configuración, el idioma elegido, etcétera.
Ver también:
Y cada vez que visito la página, la lista de productos se creará a partir de las últimas entradas en la base de datos. ¿Cómo puedo manejar esto?
Normalmente usa la solicitud o ve el alcance para ello. La carga de la lista debe realizarse en un método @PostConstruct
. Si la página no contiene ningún <h:form>
, entonces el alcance de la solicitud es correcto. Un bean de ámbito de vista se comportaría como un pedido con alcance cuando no hay <h:form>
todos modos.
Todos los enlaces / botones "ver producto" y "editar producto" que acaban de recuperar información (es decir, idempotente) deberían ser simplemente GET <h:link>
/ <h:button>
donde pasa el identificador de entidad como parámetro de solicitud por <f:param>
.
Todos los enlaces / botones "eliminar producto" y "guardar producto" que manipularán información (es decir, no idempotentes) deben realizar POST por <h:commandLink>
/ <h:commandButton>
(no desea que sean bookmarkable / searchbot -indexable!). Esto a su vez requiere una <h:form>
. Para preservar los datos para validaciones y solicitudes ajax (para que no tenga que volver a cargar / preinicializar la entidad en cada solicitud), preferiblemente el bean debe tener un alcance de vista.
Tenga en cuenta que básicamente debe tener un bean por separado para cada vista y también tenga en cuenta que esos beans no necesariamente necesitan referencia entre sí.
Entonces, dada esta entidad de "producto":
@Entity
public class Product {
@Id
private Long id;
private String name;
private String description;
// ...
}
Y este "servicio de productos" EJB:
@Stateless
public class ProductService {
@PersistenceContext
private EntityManager em;
public Product find(Long id) {
return em.find(Product.class, id);
}
public List<Product> list() {
return em.createQuery("SELECT p FROM Product p", Product.class).getResultList();
}
public void create(Product product) {
em.persist(product);
}
public void update(Product product) {
em.merge(product);
}
public void delete(Product product) {
em.remove(em.contains(product) ? product : em.merge(product));
}
// ...
}
Puede tener este "ver productos" en /products.xhtml
:
<h:dataTable value="#{viewProducts.products}" var="product">
<h:column>#{product.id}</h:column>
<h:column>#{product.name}</h:column>
<h:column>#{product.description}</h:column>
<h:column>
<h:link value="Edit" outcome="/products/edit">
<f:param name="id" value="#{product.id}" />
</h:link>
</h:column>
</h:dataTable>
@Named
@RequestScoped
public class ViewProducts {
private List<Product> products; // +getter
@EJB
private ProductService productService;
@PostConstruct
public void init() {
products = productService.list();
}
// ...
}
Y puede tener este "producto de edición" en /products/edit.xhtml
:
<f:metadata>
<f:viewParam name="id" value="#{editProduct.product}"
converter="#{productConverter}" converterMessage="Unknown product, please use a link from within the system."
required="true" requiredMessage="Bad request, please use a link from within the system."
/>
</f:metadata>
<h:messages />
<h:form rendered="#{not empty editProduct.product}>
<h:inputText value="#{editProduct.product.name}" />
<h:inputTextarea value="#{editProduct.product.description}" />
...
<h:commandButton value="save" action="#{editProduct.save}" />
</h:form>
@Named
@ViewScoped
public class EditProduct {
private Product product; // +getter +setter
@EJB
private ProductService productService;
public String save() {
productService.save(product);
return "/products?faces-redirect=true";
}
// ...
}
Y este convertidor para <f:viewParam>
de "editar producto":
@Named
@RequestScoped
public class ProductConverter implements Converter {
@EJB
private ProductService productService;
@Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
if (value == null || value.isEmpty()) {
return null;
}
try {
Long id = Long.valueOf(value);
return productService.find(id);
} catch (NumberFormatException e) {
throw new ConverterException("The value is not a valid Product ID: " + value, e);
}
}
@Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
if (value == null) {
return "";
}
if (value instanceof Product) {
Long id = ((Product) value).getId();
return (id != null) ? String.valueOf(id) : null;
} else {
throw new ConverterException("The value is not a valid Product instance: " + value);
}
}
}
Ver también:
Como una pequeña mejora de lo que BalusC recomienda, a veces puede eliminar la parte de / requiredMessage
de <f:viewParam>
de su pantalla de "detalles" y en su lugar usar la representación condicional del formulario de edición (como lo hizo BalusC) con una condición inversa para recomendar un enlace específico para la pantalla "lista / principal" o, incluso, usar una vistaAcción que probaría el parámetro y forzaría un redireccionamiento a esa lista.