I'm creating a JSF 2.2 CRUD page, using primefaces datatable together with RowEditor.
My problem lies in editing the Row. I use 2 different listeners to compare the record before and after the edit (to make sure there are any modifications, etc)
To do so, I'm making use of RowEditInit to get the Initial value, as soon as the user clicks on the pencil icon.
After the user changes the row data, I use the RowEdit event to make some checks and compare the before and after values, but my initial object ends up getting the same values the user typed after clicking save.
<h:form id="edicao" prependId="false">
<p:growl id="msgs" showDetail="true" globalOnly="true" escape="true"/>
<p:dataTable id="dataTable" var="linha" value="#{perfilMB.listaPerfilEntities}"
editable="true" draggableColumns="true" rendered="#{not empty perfilMB.listaPerfilEntities}"
widgetVar="tablePerfil" filteredValue="#{perfilMB.filteredPerfilEntities}"
emptyMessage="Não existem registros." resizableColumns="true" styleClass="datatable_cadastro" >
<f:facet name="header">
<span class="titulo_datatable" >Tabela Perfil</span>
<p:commandButton id="toggler" type="button" value="Colunas" />
<p:columnToggler datasource="dataTable" trigger="toggler" />
</f:facet>
<p:ajax event="rowEdit" listener="#{perfilMB.onRowEdit}" update=":edicao:msgs" />
<p:ajax event="rowEditCancel" listener="#{perfilMB.onRowCancel}" update=":edicao:msgs" />
<p:ajax event="rowEditInit" listener="#{perfilMB.rowEditInit}" />
<p:column headerText="ID" sortBy="#{linha.id}"
style="width:50px;">
<p:outputLabel value="#{linha.id}" style="width:100%" />
</p:column>
<p:column headerText="NOME" filterBy="#{linha.perfil}"
filterMatchMode="contains" sortBy="#{linha.perfil}"
style="width:100%;">
<p:cellEditor>
<f:facet name="output">
<h:outputText value="#{linha.perfil}" />
</f:facet>
<f:facet name="input">
<p:inputTextarea value="#{linha.perfil}" id="nome"
update=":edicao" style="width:100%" />
</f:facet>
</p:cellEditor>
</p:column>
<p:column headerText="DETALHAMENTO"
style="width:100%;"
sortBy="#{linha.descritivo}" filterBy="#{linha.descritivo}"
filterMatchMode="contains">
<p:cellEditor>
<f:facet name="output">
<h:outputText value="#{linha.descritivo}" />
</f:facet>
<f:facet name="input">
<p:inputTextarea value="#{linha.descritivo}"
id="detalhamento" update=":edicao" />
</f:facet>
</p:cellEditor>
</p:column>
<p:column headerText="ATIVO" style="width:150px;" filterBy="#{linha.ativo}" filterMatchMode="equals">
<f:facet name="filter">
<p:selectOneMenu onchange="PF('tablePerfil').filter()" >
<f:converter converterId="javax.faces.Character"/>
<f:selectItem itemLabel="TODOS" itemValue=""/>
<f:selectItem itemLabel="ATIVO" itemValue="T"/>
<f:selectItem itemLabel="INATIVO" itemValue="F"/>
</p:selectOneMenu>
</f:facet>
<p:cellEditor>
<f:facet name="output">
<p:selectBooleanCheckbox value="#{perfilMB.charToBool(linha.ativo)}" disabled="true"/>
</f:facet>
<f:facet name="input">
<p:selectBooleanCheckbox value="#{perfilMB.ativo}" id="ativo" immediate="true" update=":edicao" style="width:100%"/>
</f:facet>
</p:cellEditor>
</p:column>
<p:column headerText="!" style="width:40px">
<p:rowEditor />
</p:column>
<p:column headerText="X" style="width:40px">
<p:commandLink styleClass="ui-icon ui-icon-trash" action="#{perfilMB.preparaParaDeletar(linha)}" update=":confirmacao_exclusao" oncomplete="PF('confirmacao').show()" />
</p:column>
</p:dataTable>
</h:form>
@ViewScoped
Imports
import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
import javax.faces.context.FacesContext;
import org.primefaces.event.RowEditEvent;
1 - User Clicks the Edit Row Button, triggering RowEditInit.
2 - The Object perfilAntes gets the initial value from that row.
public void rowEditInit(RowEditEvent event) {
Object value = event.getObject();
perfilAntes = new PerfilEntity();
perfilAntes = (PerfilEntity) value;
}
3 - After the user finishes editing the data, he clicks on save, that calls the RowEdit Method.
4 - A new object names perfilTela gets the new value user has typed.
5 - Call the validaOnRowEdit method that compare the two objects against each other.
public void onRowEdit(RowEditEvent event) {
context = FacesContext.getCurrentInstance();
Object value = event.getObject();
PerfilEntity perfilTela = (PerfilEntity) value;
if(perfilTela.getId() != null){
/* Calls the validation method */
boolean validado = validaOnRowEdit(perfilTela);
/* if its all Good, saves the data on database*/
if(validado){
/*removed code*/
}
}
This method is comparing the two objects, PerfilAntes (The initial value) with the PerfilTela (The value the user typed).
When I inspect the object values, the PerfilAntes Object loses its initial value and it's equal to PerfilTela Object!
public boolean validaOnRowEdit(PerfilEntity perfilTela){
if(perfilAntes.getId() != null){
if( perfilTela.getPerfil().trim() != null && perfilTela.getDescritivo().trim() != null ){
if( perfilTela.getPerfil().equals(perfilAntes.getPerfil() )
&& perfilTela.getDescritivo().equals(perfilAntes.getDescritivo() )
&& perfilTela.getAtivo() == perfilAntes.getAtivo() ){
perfilTela = perfilAntes;
context.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO, "Changes Canceled" ,"There was no changes in row <br/><b>Case Insensitive<b/>" ));
return false;
} else {
return true;
}
} else {
perfilTela = perfilAntes;
context.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO, "Changes canceled" ,"No field can be empty or contain spaces" ));
return false;
}
}
}
I found out what the problem was.
When using Object variables, and you create objects and set values like this:
Object object1 = new Object();
Object object2 = new Object();
object1 = object2;
The objects variables don't save the object values inside these variables, instead they save only the reference between the variable in the Stack and Heap memories.
So when I set: object1 = object2;
The object1 is receiving a "pointer" to the object set on object2, and if I edit one of the attributes using either one of these variables, both will be changed because it contains only a link.
And because I set object1 = object2, the previously linked object on object1 that exists on the Heap can't be used anymore and will be cleaned by the Garbage Collector.
So I ended up setting the values using setters.