Creación de páginas de detalles maestros para las entidades, cómo vincularlas y qué ámbito de bean elegir

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 scope de la sesión en la interfaz de usuario maestra de detalles de dos páginas?

¿Cuál es el uso correcto del scope 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:

  • ¿Cómo elegir el scope del frijol correcto?

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 scope para ello. La carga de la lista debe realizarse en un método @PostConstruct . Si la página no contiene ningún , entonces el scope de la solicitud es correcto. Un bean de ámbito de vista se comportaría como un pedido con scope cuando no hay 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 / donde pasa el identificador de entidad como parámetro de solicitud por .

Todos los enlaces / botones “eliminar producto” y “guardar producto” que manipularán información (es decir, no idempotentes) deben realizar POST por / (no desea que sean bookmarkable / searchbot -indexable!). Esto a su vez requiere una . 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 scope 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 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 :

  #{product.id} #{product.name} #{product.description}       
 @Named @RequestScoped public class ViewProducts { private List 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 :

       ...   
 @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 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:

  • ¿Cómo navegar en JSF? Cómo hacer que la URL refleje la página actual (y no la anterior)
  • Controlador JSF, Servicio y DAO
  • Capa de servicio JSF
  • Cómo inyectar @EJB, @PersistenceContext, @Inject, @Autowired, etc. en @FacesConverter?
  • Comunicación en JSF 2.0 – Contiene varios ejemplos / sugerencias

Como una pequeña mejora de lo que BalusC recomienda, a veces puede eliminar la parte de / requiredMessage de 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.