I'm using cell editor in Primefaces to update cells in a datatable. However I want to validate the input before I confirm the change.
I have used FacesContext.getCurrentInstance().validationFailed(); for this purpose but still getting the cell updated.
This is how I'm implementing it:
<p:dataTable value="#{bean.list}" var="var" id="table" editMode="cell" editable="true">
<p:ajax event="cellEdit" listener="#{bean.onCellEdit}" update="#form"/>
<p:column headerText="Quantity">
<p:cellEditor>
<f:facet name="output">
<h:outputText value="#{var.quantity}"/>
</f:facet>
<f:facet name="input">
<p:inputText value="#{var.quantity}"/>
</f:facet>
</p:cellEditor>
</p:column>
</p:dataTable>
Bean Method:
public void onCellEdit(CellEditEvent event) {
//validate new value
if(!validate(event.getNewValue())){
//if validation returned false stop updating the cell
FacesContext.getCurrentInstance().validationFailed();
}
}
I want to stop updating the cell if the new value did not pass the validation, but the cell gets updated anyways. How can I solve this problem?
PS: Primefaces 3.5
What you're observing happens because because the celleditor's event happens in the INVOKE_APPLICATION phase, too late for any validation failure to have any effect.
You can just use the plain validator attribute on the <p:inputText/> like you would any other JSF input component. The behaviour will be the same, regardless of the fact that it's a facet of the <p:cellEditor/>
Related
I tried c:set and ui:param, but in the end I think I did something wrong.
Here is the crucial XHTML part:
<h:form id="formTable">
<p:dataTable editable="true" value="#{types.dataList}" var="datalist" style="width:90%; margin-bottom:20px"
rows="10" paginator="true" rowsPerPageTemplate="10,25" resizableColumns="true" liveResize="true" tableStyle="width:auto">
<!-- save the old value for the edit action-->
<c:set var="oldValue" value="#{datalist.getType()}"/>
<!-- column for testing purpose-->
<p:column>
<h:outputText value="#{oldValue}" />
</p:column>
<p:column headerText="Type" sortBy="#{datalist.type}">
<p:cellEditor>
<f:facet name="output">
<h:outputText value="#{datalist.type}" />
</f:facet>
<f:facet name="input">
<h:inputText value="#{datalist.type}" binding="#{inputUpdateType}" required="true" />
</f:facet>
</p:cellEditor>
</p:column>
<p:column headerText="Date" sortBy="#{datalist.lastModifyDate}">
<h:outputText value="#{datalist.lastModifyDate}"/>
</p:column>
<p:column headerText="Edit">
<p:rowEditor rendered="#{!datalist.dependenciesFound}"/>
<h:graphicImage value="../images/attention.png" rendered="#{datalist.dependenciesFound}" />
</p:column>
<p:column headerText="Delete">
<p:commandLink action="#{types.delete(datalist.type)}" rendered="#{!datalist.dependenciesFound}" update=":formTable">
<p:graphicImage url="../images/delete.png" />
<p:confirm header="Confirmation" message="Are you sure?" icon="ui-icon-alert" />
</p:commandLink>
<h:outputText value="Not editable because of dependency" rendered="#{datalist.dependenciesFound}"/>
</p:column>
<!-- We edit/update the value here. This is where the old value is needed-->
<p:ajax event="rowEdit" listener="#{types.update(oldValue, inputUpdateType.value)}"/>
</p:dataTable>
<p:confirmDialog global="true" showEffect="fade" hideEffect="fade">
<p:commandButton value="Yes" type="button" styleClass="ui-confirmdialog-yes" icon="ui-icon-check" />
<p:commandButton value="No" type="button" styleClass="ui-confirmdialog-no" icon="ui-icon-close" />
</p:confirmDialog>
</h:form>
I hope this is somewhat understandable.
For my project I use Primefaces 6.1. To edit a row in this table I need to save the old value and use that value in the ajax call at the bottom. I need this, because the application or rather the DB table behind this demands it (id is this value).
The edited value is stored in the variable inputUpdateType.
My problem:
When I enter a new value and hit edit, the old value is the same as the edited value, when the application enters the update method. I made a log entry in my update method to verify what values are passed. Same with c:set and ui:param.
What am I doing wrong? How can I make this work?
EDIT:
Here is how I try to get the old value stored in my bean:
<p:ajax event="rowEdit" listener="#{types.update(types.dataListOld.toArray()[rowIndex].getType(), inputUpdateType.value)}"/>
So, it seems with ajax the values will be updated before executing the method. Therefore the old value is not available on server side anymore. Unfortunately storing the variable in JSF is also not possible because c:set will be processed before the JSF tags and will thus not hold any values (without a scope it will act as an alias and in the end also been updated before the method gets executed).
To make this whole thing work I used cell edit mode instead of the default row edit mode. With this I'm able to get the old and new value:
public void onCellEdit(CellEditEvent event){
//We will call this method on edits made to a cell
//Values are stored in a Arraylist. Number of entries depend on how many components there are in the edited cell/facet
//Here we have two because in "input" there are two components defined (although both are rendered at the same time).
#SuppressWarnings("unchecked")
ArrayList<String> oldValue = ((ArrayList<String>)(event.getOldValue()));
#SuppressWarnings("unchecked")
ArrayList<String> newValue = ((ArrayList<String>)(event.getNewValue()));
//In the end both entries have the same value, so we just take the first.
if(newValue != null && !newValue.equals(oldValue)) {
update(oldValue.get(0), newValue.get(0));
FacesMessage msg = new FacesMessage(FacesMessage.SEVERITY_INFO, "Cell Changed", "Old: " + oldValue.get(0) + ", New:" + newValue.get(0));
FacesContext.getCurrentInstance().addMessage(null, msg);
}
}
Here is why I have two entries in my values list (two components in "input"):
<f:facet name="input">
<h:inputText value="#{datalist.type}" binding="#{inputUpdateType}" required="true" rendered="#{!datalist.dependenciesFound}"/>
<h:outputText value="#{datalist.type}" rendered="#{datalist.dependenciesFound}"/>
</f:facet>
</p:cellEditor>
I have a need to display a list of Strings on a page, which a user will need to be able to add, edit, and delete entries. I figured I would use a DataTable to accomplish this.
The page appears to work properly - values are displayed in the DataTable, rows are deleted when the Delete icon is clicked, and rows are added when the Add button is clicked. However, when engaging the RowEditor by clicking the pencil icon, changes are never reflected when clicking the checkmark; the value of the row just goes back to being blank.
Here is a GIF showing the problem happening - http://imgur.com/a/dxlht
I've copied the majority of the DataTable markup from other pages in my project, which all work properly. The only difference I can see here is that this is a DataTable of Strings, not a complex object. Does this affect the "input" facet of the RowEditor when the value of the InputText is just a simple String?
Here is the DataTable markup:
<h:form>
<p:dataTable id="configTable" value="#{bean.configs}" var="config"
editable="true" editMode="row" emptyMessage="No entries exist.">
<p:column>
<p:cellEditor>
<f:facet name="output"><h:outputText value="#{config}" /></f:facet>
<f:facet name="input"><p:inputText value="#{config}" /></f:facet>
</p:cellEditor>
</p:column>
<p:column>
<p:rowEditor />
</p:column>
<p:column>
<p:commandLink update="configTable" styleClass="ui-icon ui-icon-trash" process="#this"
actionListener="#{bean.removeEntry(config)}" />
</p:column>
<f:facet name="footer">
<p:commandButton value="Add Entry" update="configTable" icon="ui-icon-plus" style="float:right;" process="#this"
actionListener="#{bean.addEntry()}" />
</f:facet>
</p:dataTable>
</h:form>
Here is the relevant bean code:
private List<String> configs;
public List<String> getConfigs() {
return this.configs;
}
public void setConfigs(List<String> configs) {
this.configs = configs;
}
public void removeEntry(String entry) {
this.configs.remove(entry);
}
public void addEntry() {
this.configs.add("");
}
I am using PrimeFaces 5.3/JSF 2.0.
Please note that when you press the check mark (to finish editing a row) JSF calls the setter method of whatever class your table rows are.
String class doesn't have a setter method, thus you will need to create a wrapper class with just one field (with getter and setter) and change your configs object to a List of yourClass objects.
I want to use the PF 4 celleditor and sticked to the example in the showcase. But I get the following behavior:
I can edit my cell, the onCellEdit() inside the bean is called, but the event will contain the old value for event.getNewValue(). Listening to the network traffic, I was able to catch this:
javax.faces.partial.ajax=true&
javax.faces.source=pvChangeForm%3Apvc&
javax.faces.partial.execute=pvChangeForm%3Apvc&
javax.faces.partial.render=pvChangeForm%3Apvc+pvForm&
javax.faces.behavior.event=cellEdit&
javax.faces.partial.event=cellEdit&
pvChangeForm%3Apvc_encodeFeature=true&
pvChangeForm%3Apvc_cellInfo=0%2C1&
pvChangeForm%3Apvc%3A0%3Aj_idt127=pvChangeForm%3Apvc%3A0%3Aj_idt127&
pvChangeForm=pvChangeForm&pvChangeForm%3Apvc%3A0%3Aj_idt130=666&
javax.faces.ViewState=-8810553618561534598%3A1979735468348742605
where the important line is the second to last. 666 is the value I put into the cell. It is also displayed if I edit this cell again. But leaving the cell or pressing Enter, it is not saved.
My datatable:
<h:form id="pvChangeForm">
<p:dataTable id="pvc" var="tVar" value="#{paramBean.pvForChange.values}" editable="true" editMode="cell">
<p:ajax event="cellEdit" listener="#{paramBean.onCellEdit}" update=":pvChangeForm:pvc" />
<p:column>
<p:cellEditor>
<f:facet name="output"><h:outputText value="#{tVar}" /></f:facet>
<f:facet name="input"><p:inputText value="#{tVar}" style="width:96%" label="Wert"/></f:facet>
</p:cellEditor>
</p:column>
</p:dataTable>
</h:form>
paramBean.pvForChange.values is a List<String>. Somehow I have the feeling that the problem lies within this fact (because I want to edit a String in a list directly). But from my understanding, there shouldn't be a problem with that.
Yes, the issue is trying to update a value not wrapped in a POJO. Primefaces datatable are meant to work with the properties wrapped in POJOs, but that said, you can fix your issue using your bean as a wrapping object, like this
<h:form id="pvChangeForm">
<p:dataTable id="pvc" var="tVar" value="#{paramBean.pvForChange.values}" rowIndexVar="index" editable="true" editMode="cell">
<p:ajax event="cellEdit" listener="#{paramBean.onCellEdit}" update=":pvChangeForm:pvc" />
<p:column>
<p:cellEditor>
<f:facet name="output"><h:outputText value="#{paramBean.pvForChange.values[index]}" /></f:facet>
<f:facet name="input"><p:inputText value="#{paramBean.pvForChange.values[index]}" style="width:96%" label="Wert"/></f:facet>
</p:cellEditor>
</p:column>
</p:dataTable>
</h:form>
I have a table with a date column that always null. The data is a LazyDataModel child. Also i have row editing.
<p:dataTable id="dataTable" var="Var" value="#{tableBean.model}"
lazy="true"...........
<p:ajax event="rowEdit" listener="#{tableBean.onRowEdit}"
update=":dataTableForm:messages"/>
<p:column sortBy="VarName">
<f:facet name="header">
<h:outputText value="#{msg['Var.table.header.assignee']}"/>
</f:facet>
<p:cellEditor>
<f:facet name="output">
<h:outputText value="#{Var.Name}"/>
</f:facet>
<f:facet name="input">
<h:inputText id="assigneeNameInput" styleClass="row-input" value="#{Var.Name}"/>
<p:message for="assigneeNameInput"/>
</f:facet>
</p:cellEditor>
</p:column>
<f:facet name="header">
<h:outputText value="#{msg['var.table.header.action']}"/>
</f:facet>
<p:rowEditor/>
</p:column>
I need to delete edited row if date column was filled.
I try this
public void onRowEdit(RowEditEvent event) {
Var updatedVar = (Var) event.getObject();
if (updatedVar.getReturnDate() != null) {
updatedVar = null;
}
}
And this
public void onRowEdit(RowEditEvent event) {
Var updatedVar = (Var) event.getObject();
if (updatedVar.getReturnDate() != null) {
((List<T>) getWrappedData()).remove(oldEntry);
}
}
Both attempts did not work, only if update table twice via remoteCommand . Suggest the decision. Thanks!
Edit: Data removed from lazydatamodel, but on page i still had updated row.
Edit : JSF application lifecycle consist of six phases.
Phase 4:Update model values :
After the JSF checks that the data is valid, it walks over the component tree and set the corresponding server-side object properties to the components' local values. The JSF will update the bean properties corresponding to input component's value attribute.
Phase 5: Invoke application :
During this phase, the JSF handles any application-level events, such as submitting a form / linking to another page.
So, invocation onRowEdit() method occur after dataTable's update.
I have a datatable with primefaces, loaded about three records, it happens that I have in a column one inputText, it happens that the button is outside the datable record, and click the button I want to record, capture me inputText values, and to update records each dataTable.
<p:dataTable id="dataTable" var="confParamGen" value="#{regRolMB.paramLdap}"
rowIndexVar="rowIndex">
<p:column>
<f:facet name="header" >
<h:outputText value="N°" />
</f:facet>
<h:outputText value="#{rowIndex+1}" />
</p:column>
<p:column>
<f:facet name="header">
<h:outputText value="Number Long" />
</f:facet>
<h:outputText value="#{confParamGen.numberCort}" />
</p:column>
<p:column>
<f:facet name="header">
<h:outputText value="Value Role" />
</f:facet>
<p:inputText value="#{confParamGen.valuesRole}" style="width: 200px;" />
</p:column>
</p:dataTable>
<p:commandButton value="Save" rendered="#{regRolMB.showButtonUpdate}"
actionListener="#{regRolMB.actualizarRol}" styleClass="positionButton">
<f:attribute name="confParamGen" value="#{confParamGen}" />
</p:commandButton>
In the controller I have it so, but it falls to cast the Arraylist.
public void updateRol(ActionEvent event) {
List<DateGeneral> rolConPar = new ArrayList<DateGeneral>();
rolConPar = ((ArrayList<DateGeneral>) event.getComponent().getAttributes().get("confParamGen"));
for(DateGeneral dato: rolConPar){
System.out.println("===> "+dato.getValuesRole());
}
}
I get this error, although the problem is not the modified data capture of inputText, only captures the data loaded from DataTable
java.lang.ClassCastException: com.bbva.sca.adm.bean.DatoGeneral cannot be cast to java.util.ArrayList
The ClassCastException is being thrown because you've actually set an instance of DatoGeneral as attribute here:
<f:attribute name="confParamGen" value="#{confParamGen}" />
This is clearly not a List<DatoGeneral> (or List<DateGeneral> or whatever typo you made during careless oversimplifying/translating of the code; just use English all the time in code). Technically, you can solve it by passing the list itself instead:
<f:attribute name="confParamGen" value="#{regRolMB.paramLdap}" />
After all, this approach isn't making any sense. Your sole purpose seems to be just collecting the submitted values. In that case, you seem to be completely new to JSF and not yet fully understand why you're using JSF and what it is all capable of. JSF has already updated the model values with the submitted values. You just have to access the very same list behind <p:dataTable value> directly.
public void actualizarRol(ActionEvent event) {
for(DateGeneral dato: paramLdap){
System.out.println("===> "+dato.getValuesRole());
}
}
This way you can just get rid of the whole <f:attribute>.
My datatable load it like this:
public ArrayList<DatoGeneral> getParamLdap() {
try{
if(codSistema != null){
confParamGen = new ArrayList<DatoGeneral>();
confParamGen = datoGeneralService.obtenerParamGen(sistema.getConfLdap().getCdCodigo());
}
}catch(Exception e){
e.printStackTrace();
}
return (ArrayList<DatoGeneral>) confParamGen;
}