Primefaces datatable used to display non updateable database view results? - jsf

Good day
Is it possible (I seem to be stuck at not being able to click the 'tick' to accept a row edit) to use a Primefaces datatable to display the result of a non updateable Postgres view, but have custom code in the onRowEdit event that will persist the changes correctly to the DB?
Or MUST a Primefaces / JSF datatable be linked to an updateable entity mapping in order for it to be used to 'update' and display info?
I have in the .xhtml file this:
<p:dataTable id="conf_data_table" var="confRoomBookingDisplay" style="width: auto" rowIndexVar="rowid" editingRow="true"
value="#{conferenceRoomBookingView.conferenceRoomBookingList}" editable="true"
editMode="row" widgetVar="cellBookings">
<f:facet name="header">
Venue Booking
</f:facet>
<p:ajax event="rowEdit" listener="#{conferenceRoomBookingView.onRowEdit}"
update=":create_new_booking:growl"/>
<p:ajax event="rowEditCancel" listener="#{conferenceRoomBookingView.onRowCancel}"
update=":create_new_booking:growl"/>
<p:column headerText="Booking Ref" style="width: 10%; text-align: center">
<p:outputLabel value="#{ConferenceRoomBooking.conferenceRoomBookingAid}"
style="text-align: center"/>
</p:column>
<p:column headerText="Time Slot" style="width: 10%; text-align: center">
<p:outputLabel value="#{ConferenceRoomBooking.timeSlot}" style="text-align: center"/>
</p:column>
<p:column headerText="Booked For" style="width: 15%; alignment: center">
<p:outputLabel value="#{ConferenceRoomBooking.empShortNameSurname}"
style="text-align: left"/>
</p:column>
<p:column headerText="Booking Comment" style="width: 60%; alignment: center">
<p:cellEditor>
<f:facet name="output">
<h:outputText value="#{ConferenceRoomBooking.conferenceRoomBookingComment}"/>
</f:facet>
<f:facet name="input">
<p:inputText value="#{ConferenceRoomBooking.conferenceRoomBookingComment}"
style="width:98%"/>
</f:facet>
</p:cellEditor>
</p:column>
<p:column style="width: 5%">
<p:rowEditor />
</p:column>
</p:dataTable>
And then on my #RequestScoped (javax.enterprise.context.RequestScoped) #Named backing bean (I tried it with a #ViewScoped - org.omnifaces.cdi annotation as well).
The event code looks like this:
public void onRowEdit(RowEditEvent event) {
conferenceRoomBookingAid = (((ConferenceRoomBookingEntity) event.getObject()).getConferenceRoomBookingAid());
String msgEdit = Long.toString(conferenceRoomBookingAid);
FacesMessage msg = new FacesMessage("Booking Edited for Reference ID:", msgEdit);
FacesContext.getCurrentInstance().addMessage(null, msg);
confRoomService.persistComment(How do I get my updated value from the event???);
}
public void onRowCancel(RowEditEvent event) {
conferenceRoomBookingAid = (((ConferenceRoomBookingEntity) event.getObject()).getConferenceRoomBookingAid());
String msgEdit = Long.toString(conferenceRoomBookingAid);
FacesMessage msg = new FacesMessage("Booking Cancelled for Reference ID: ", msgEdit);
FacesContext.getCurrentInstance().addMessage(null, msg);
}
Currently commenting out the confRoomService.persistComment(How do I get my updated value from the event???); code, gets me to a properly rendered table where I am able to edit the row. The problem however is that I can not click the 'accept' / 'yes' tick with the mouse, and pressing enter just clears the cell again.
The reason I am using a non updateable view is that the current DB structure uses foreign keys to link the data to persons, and using the base entity will display the foreign key id instead of its value.

After a lot of different versions of Entities and database views, I came to the conclusion that the JSF / Primefaces datatable component is meant to be used:
When linked directly via an Entity that is mutable, and thus a 100% representation of the underlying database table, with no custom views of the data, full editing is allowed and works as advertised.
When linked to an Entity that is based on a Database View (updateable or not), the cells / rows of the datatable cannot be edited in the datatable cellEdit or rowEdit events.
I would be ecstatic if proved wrong on the above, but for now have made peace with using the datatable as immutable when based on a database view.

Related

Way to store variable in JSF / issue with c:set and ui:param

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>

DataTable of Strings with a RowEditor will not save changes

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.

Ajax#updateColumn() doesn't update cell

I've added OmniFaces to my Primefaces application primarily for being able to update a single row of a dataTable. I'm calling Ajax.updateColumn(dataTable, 1) on push event, but nothing gets updated. Eg. one of my cells is like this
<p:column style="width:40px;text-align: center;"
toggleable="false" styleClass="smallPadding unmarkable">
<f:facet name="header">
<p:commandLink styleClass="ui-icon ui-icon-car"
style="display: inline-flex;" id="carIcon" />
</f:facet>
<p:tooltip for="carIcon" value="Anzahl freie Fahrzeuge" />
<h:outputText
value="#{editLoad.getAmountOfUnusedOrderPositions(order)}" />
<h:outputText value="/#{order.orderPositions.size()}" />
</p:column>
I've debugged and #{editLoad.getAmountOfUnusedOrderPositions(order))} returns 0, but the wrong value (8) is displayed after the update.
From the Ajax#updateColumn() javadoc:
public static void updateColumn(UIData table, int index)
...
Note that the to-be-updated direct child of UIColumn must be a fullworthy JSF UI component which renders a concrete HTML element to the output, so that JS/ajax can update it. So if you have due to design restrictions for example a <h:panelGroup rendered="..."> without an ID, then you should give it an ID. This way it will render a <span id="..."> which is updateable by JS/ajax.
Your <h:outputText> of interest doesn't have a id. Give it one.
<h:outputText id="amountOfUnusedOrderPositions"
value="#{editLoad.getAmountOfUnusedOrderPositions(order)}" />

Unable to Navigate from a Command Link in a DataModel Table Primefaces,JSF

I have a DataModel Table with Command Links to Navigate to another Page On Clicking.
But the Navigation is not Occurring in DataModel Table, as when i click on the link it is selecting the whole Row as it is a DataModel.
What should i do in-order to make it work as a Command Link.
My Piece of Code:
<p:dataTable id="studentDtTble" var="studmodel" value="#{studentController.dataModelList}">
<p:columnGroup type="header">
<p:row>
<p:column headerText="StudentId"></p:column>
<p:column headerText="StudentName"></p:column>
<p:column headerText="Add" ></p:column>
</p:row>
</p:columnGroup>
<p:column>
<p:commandLink id="ajax" update="">
<h:outputText value="#{studmodel.studentId}" styleClass="txtlink" />
</p:commandLink>
</p:column>
<p:column>
<h:outputText value="#{studmodel.studentName}" />
</p:column>
<p:column >
<p:selectBooleanCheckbox value="#{studmodel.add}" />
</p:column>
</p:dataTable>
When i Click on the StudentId Link then it Should navigate to Student Information.jsf Page.
Which is not Happening.
For that to happen i Wrote a Method in Controller
studentInfo(studentID){
..Navigate to the Student Info Page ..
}
But i am unable to Call the Method with that studentId because it is selecting as a row . Any Suggestions
Selected whole row as you click on it is a default behaviour of the datatable. You can use selection attribute of the datatale and pointing it to an object like this
<p:dataTable id="studentDtTble" var="studmodel" value=#{studentController.dataModelList}"
selection="#{studentController.selectedItem}>
and in the controller you should have a mapping element Student selectedItem, I am assuming Student as your object. Your method should have a String as a return type to perform navigation. you can define a navigation rule in faces-config.xml.
Or
You can use action attribute of the command Link(action method must have String as a return type) and pass student id like below using
<f:setPropertyctionListener value="#{studmodel.studentId}" target="any string
in controller /> as a child element in commandLink
Hope this hepls :)

Consultation List cast an object in Primefaces?

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;
}

Resources