0

I'm learning about JSF and am trying to do a tabbed pane following this tutorial:

Tab manager using Ajax and JSF

I have managed to get the tab switch working. Now I want to include a form defined in another XHTML file as tab for this tabbed pane in which there's a dataTable with a commandButton to delete the selected row, called clientes.xhtml. If I navigate directly to this page then delete button works as expected. But when I include this page within contentForm it shows as expected but delete button doesn't do what is supposed to do, it just refresh the current page but no row is deleted.

This is what I have so far:

welcome.xhtml

<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<ui:composition xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
                xmlns:h="http://xmlns.jcp.org/jsf/html"
                xmlns:f="http://xmlns.jcp.org/jsf/core"
                xmlns="http://www.w3.org/1999/xhtml"
                template="./templates/BasicTemplate.xhtml">

    <ui:define name="menu_bar">
        <h:form id="formMenu">            
            <ul id="menu-list"> 
                <li><h:commandLink value="Home">
                        <f:ajax event="click" render=":contentForm" listener="#{tabViewManagedBean.setTabIndex(0)}" />
                    </h:commandLink></li>
                <li><h:commandLink value="Clientes">
                        <f:ajax event="click" render=":contentForm" listener="#{tabViewManagedBean.setTabIndex(1)}" />
                    </h:commandLink></li>
                <li><h:commandLink value="Proveedores">
                        <f:ajax event="click" render=":contentForm" listener="#{tabViewManagedBean.setTabIndex(2)}" />
                    </h:commandLink></li>
            </ul>
        </h:form>
    </ui:define>

    <ui:define name="content">
        <h:form id="contentForm">
            <h:panelGroup layout="block" rendered="#{tabViewManagedBean.tabIndex == 0}">
                <h1>Hi there!</h1>
                <hr />
            </h:panelGroup>

            <h:panelGroup layout="block" rendered="#{tabViewManagedBean.tabIndex == 1}">
                <ui:include src="clientes.xhtml" />
            </h:panelGroup>

            <h:panelGroup layout="block" rendered="#{tabViewManagedBean.tabIndex == 2}">
                <ui:include src="proveedores.xhtml" />
            </h:panelGroup>
        </h:form>
    </ui:define>
</ui:composition>

clientes.xhtml

<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<ui:composition xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
                xmlns:h="http://xmlns.jcp.org/jsf/html"
                xmlns:f="http://xmlns.jcp.org/jsf/core"
                xmlns="http://www.w3.org/1999/xhtml">
    <h:form>
        <h:dataTable id="dataTable" value="#{clientesManagedBean.listaClientes}" var="cliente">
            <h:column>
                <f:facet name="header">
                    <h:outputText value="Id" />
                </f:facet>
                <h:outputText value="#{cliente.idCliente}" />
            </h:column>

            <h:column>
                <f:facet name="header">
                    <h:outputText value="Fecha de ingreso" />
                </f:facet>
                <h:outputText value="#{cliente.fechaIngreso}">
                    <f:convertDateTime pattern="dd/MM/yyyy"/>
                </h:outputText>
            </h:column>

            <h:column>
                <f:facet name="header">
                    <h:outputText value="Nombre" />
                </f:facet>
                <h:outputText value="#{cliente.nombre}" />
            </h:column>

            <h:column>
                <f:facet name="header">
                    <h:outputText value="Domicilio" />
                </f:facet>
                <h:outputText value="#{cliente.domicilio}" />
            </h:column>

            <h:column>
                <f:facet name="header">
                    <h:outputText value="Teléfono" />
                </f:facet>
                <h:outputText value="#{cliente.telefono}" />
            </h:column>

            <h:column>
                <f:facet name="header" />
                <h:commandButton image="./resources/css/delete_16.png" action="#{clientesManagedBean.eliminarCliente(cliente)}"/>
            </h:column>

        </h:dataTable>
    </h:form>
</ui:composition>

Edit 1

Here is the ClientesManagedBean code:

ClientesManagedBean.java

import beans.interfaces.IClientesBeanLocal;
import domain.entities.ClienteJpa;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.ejb.EJB;
import javax.faces.bean.ManagedBean;
import javax.faces.view.ViewScoped;    

@ManagedBean
@ViewScoped
public class ClientesManagedBean {

    @EJB(beanName = "ClientesBeanJpa")
    private IClientesBeanLocal clientesBeanLocal;

    private List<ClienteJpa> listaClientes;
    private ClienteJpa cliente;

    @PostConstruct
    public void init() {
        listaClientes = new ArrayList<>();
        listaClientes.addAll(clientesBeanLocal.getTodos());
    }        

    public List<ClienteJpa> getListaClientes() {
        return listaClientes;
    }        

    public ClienteJpa getCliente() {
        return cliente;
    }

    public void eliminarCliente(ClienteJpa cliente) {            
        if(clientesBeanLocal.eliminar(cliente) == IClientesBeanLocal.EXITO) {
            listaClientes.remove(cliente);
        }
    }

}

And ClientesBeanJpa session bean, just in case:

ClientesBeanJpa.java

import beans.interfaces.IClientesBeanLocal;
import domain.entities.ClienteJpa;
import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

@Stateless(name = "ClientesBeanJpa")
public class ClientesBeanJpa implements IClientesBeanLocal {

    @PersistenceContext(unitName = "CursoJ2eePU")
    private EntityManager entityManager;

    @Override
    @TransactionAttribute(TransactionAttributeType.REQUIRED)
    public int eliminar(ClienteJpa cliente) {
        if(entityManager == null) {
            String error = "Error al inyectar EntityManager en la clase " + getClass().getCanonicalName();
            throw new ExceptionInInitializerError(error);
        } else {
            ClienteJpa clienteAEliminar = entityManager.getReference(ClienteJpa.class, cliente.getIdCliente());
            entityManager.remove(clienteAEliminar);
            return EXITO;
        }
    }
}

Edit 2

Based on @Luiggi's suggestion I've tested if ClientesManagedBean#eliminar(cliente) method is even called and I've found this out:

  • If tabIndex property is set to 1 by default, then clientes.xhtml is rendered and it works as expected.
  • If tabIndex property is set to another value and then navigate to tab 1, then eliminar(cliente) is not even called.

Including TabViewManagedBean code just in case.

TabViewManagedBean.java

import javax.faces.bean.ManagedBean;
import javax.faces.view.ViewScoped;

@ManagedBean
@ViewScoped
public class TabViewManagedBean {

    private Integer tabIndex = 0;

    /*
     * If I set tabIndex to 1 then clientes.xhtml is rendered by default
     * and everithing works as expected.
     * But if I set this property to 0 and then navigate to tab 1 then it
     * behaves as described.
     */

    public TabViewManagedBean() {
        super();
    }

    public Integer getTabIndex() {
        return tabIndex;
    }

    public void setTabIndex(Integer tabIndex) {
        this.tabIndex = tabIndex;
    }

}
dic19
  • 17,821
  • 6
  • 40
  • 69

1 Answers1

3

The problem is that you're nesting <form>, which is invalid HTML. This can be noted by this code:

welcome.xhtml:

<ui:define name="content">
    <h:form id="contentForm">
        <!-- code here... -->
        <h:panelGroup layout="block" rendered="#{tabViewManagedBean.tabIndex == 1}">
            <!-- including source of clientes.xhtml page -->
            <ui:include src="clientes.xhtml" />
        </h:panelGroup>
        <!-- code here... -->
    </h:form>
</ui:define>

And in clientes.xhtml you have:

<h:form>
    <h:dataTable id="dataTable" value="#{clientesManagedBean.listaClientes}" var="cliente">
        <!-- more code... -->
    </h:dataTable>
<h:form>

Which ends in this way:

<h:form id="contentForm">
    <!-- code here... -->
    <h:panelGroup layout="block" rendered="#{tabViewManagedBean.tabIndex == 1}">
        <h:form>
            <h:dataTable id="dataTable" value="#{clientesManagedBean.listaClientes}" var="cliente">
                <!-- more code... -->
            </h:dataTable>
        <h:form>
    </h:panelGroup>
    <!-- code here... -->
</h:form>

Decide where to define the <h:form> to not have nested forms. IMO you should define the <h:form> in the narrowest possible scope, which in this case will be in clientex.xhtml page only (and in the pages to include).

More info:

Community
  • 1
  • 1
Luiggi Mendoza
  • 85,076
  • 16
  • 154
  • 332
  • Thanks Luiggi! I get the point and I've deleted the `` in `welcome.xhtml` as suggested and replaced with a `` instead to re-render it with ajax. However still the same behaviour: everithing is correctly rendered but `commandButton` doesn't delete anything. – dic19 Jun 24 '14 at 21:44
  • @dic19 please provide the definition of `clientesManagedBean`. – Luiggi Mendoza Jun 24 '14 at 21:53
  • @dic19 by your edit, there should be no problem. Can you verify if the method is executed by printing a log message? – Luiggi Mendoza Jun 24 '14 at 22:11
  • Thanks Luiggi, I really appreciate your effort. Please see my new edit. I've tried to print a log message but no luck unless `tabIndex` is set to `1` by default. – dic19 Jun 24 '14 at 22:40
  • @dic19 Please put a `` somewhere in the page to check if there is a conversion/validation issue (which would be odd to begin with). Apart of that, I can only think that your scenario is case 7 of the link at the bottom of my answer, so I would like you to test if this effect of delete action is not invoked just for the first *click* or for several *clicks*. Sadly, I don't have a complete environment at hand to test this. – Luiggi Mendoza Jun 24 '14 at 22:44
  • Thanks Luiggi! I finally gave up with this strange issue that simply doesn't make sense. However your answer guide my in the right track about nested forms and possibly causes of not working commandButton, so I think this is the correct answer. – dic19 Jun 30 '14 at 12:17