I am trying to implement table with users' accounts, which can be modified by administrators. I use primefaces (2.2) DataTable component with cellEditor.
I have onRowEditListener which uses manageUsers.onEditRow() method to persist changes in database via UserDAO object.
After loading the page and updating table cell - data in database change properly. Unfortunately, when I am trying to update previous row again - (UserDAO)event.getObject(); returns old object (the one after first change) and data are not updated.
When I reload page (F5) and edit row - then data are changed properly.
How can I update table or how can I get the freshest version of user without reloading page?
Using Primefaces 2.2, JSF 2.1, Glassfish 3.1
PAGE:
<p:column headerText="login" filterBy="#{u.user.login}" filterMatchMode="contains" style="width:150px">
<p:cellEditor>
<f:facet name="output">
<h:outputText value="#{u.user.login}" />
</f:facet>
<f:facet name="input">
<h:form>
<p:inputText value="#{u.user.login}" />
</h:form>
</f:facet>
</p:cellEditor>
</p:column>
<p:column headerText="email" filterBy="#{u.user.email}" filterMatchMode="contains" style="width:150px">
<p:cellEditor>
<f:facet name="output">
<h:outputText value="#{u.user.email}" />
</f:facet>
<f:facet name="input">
<h:form>
<p:inputText value="#{u.user.email}" />
</h:form>
</f:facet>
</p:cellEditor>
</p:column>
//... other fields
<p:column headerText="Options">
<p:rowEditor />
</p:column>
</p:dataTable>
</h:form>
ManageBean with ApplicationScope (CDI)
#Named
#ApplicationScoped
public class ManageUsers implements Serializable {
#Inject
/** Inject database */
private DB db;
/** List with all leaked data which is loaded from database */
private List<UserDAO> users;
private User selectedUser;
private SelectItem[] manufacturerOptions;
public ManageUsers() {
manufacturerOptions = createFilterOptions();
}
public SelectItem[] getManufacturerOptions() {
return manufacturerOptions;
}
public User getSelectedUser() {
return selectedUser;
}
public void setSelectedUser(User selectedUser) {
this.selectedUser = selectedUser;
}
/** List of users loaded from database */
public void getDataFromDatabase() {
try {
users = db.getUserList();
if (users == null) {
throw new Exception("Pusta lista użytkowników");
}
} catch (Exception ex) {
FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_ERROR,
"Nie można wyświetlić tabeli wyników",
"Nie udało się pobrać danych, prosimy spróbować ponownie później.");
FacesContext.getCurrentInstance().addMessage(null, message);
}
}
/**
* Get list of leaked Data.
* If list is null then getDataFromDatabase method is used.
* #see DataExplorer.getDataFromDatabase()
*/
public List<UserDAO> getUsers() {
if (users == null) {
getDataFromDatabase();
}
return users;
}
private SelectItem[] createFilterOptions() {
SelectItem[] options = new SelectItem[3];
options[0] = new SelectItem("", "-select-");
options[1] = new SelectItem("true", "Admins");
options[2] = new SelectItem("false", "Users");
return options;
}
public void onEditRow(RowEditEvent event){
UserDAO userDAO = (UserDAO)event.getObject();
try {
userDAO.update();
} catch (UserDAOException ex) {
Log.logger.error("User not edited,"+ex);
}
//getDataFromDatabase();
}
}
You are always returning a new list which is a well known bad practice in JSF, fetching data in getter. Getters may be called several times during JSF lifecycle. Cache your user list in a variable and better to use view scope.
Inline edit/update actually works but you are losing the updated model by returning a new list every time.
I don't know if I really understood your problem but you can easily update your table or even better update your form but implementing a commandButton with update attribute:
Let's say you have a form and it's id is myTableForm, then inside that form you have your dataTable component and a commandButton component. This button will look like this:
<p:commandButton ajax='true' value='PersistData' update='myTableForm' action='if really needed' />
You can refer to an action method only if you need to. Otherwise this button will only update your entire form by implementing an ajax request. Ajax is by default true, but I added that attribute there for explicitly.
Of course you can have another logic design...you don't have to implement a button to do that, you could have some ajax listeners, some javascript functions that will be triggered at page load, edit cells etc... I have chosen a commandButton because it's the easiest way to understand the logic...
Please let me know if this was really what you wanted.
Problem solved.
There are extra h:form around p:inputText =_=
Related
my XHTML page
<p:dataTable id="idAlertsTable" widgetVar="idAlertsTable" var="alertRow"
value="#{alertsListBean.searchResult }"
filterEvent="enter" >
<p:column id="claimYear" sortBy="#{alertRow.claimYear}" filterBy="#{alertRow.claimYear}">
<h:outputText value="#{alertRow.claimYear}" />
</p:column>
<p:column
other columns..
</p:column>
I have bean for above datatable which is view scoped but I have to store this filterBy value in session,i tried using following thing to store filterBy values & setting it back to Datatable after coming back from any dialog or page
private void setFiltersInSession()
{
DataTable table = DataTable();--method for getting table
Map<String,Object> dataTableFilter=(Map<String, Object>)table.getFilters();
if(!dataTableFilter.isEmpty())
SessionUtil.setFacesSessionObject("ListFilter", dataTableFilter);
}
private void getFiltersFromSession()
{
DataTable table = DataTable();
table.setFilters((Map<String, Object>)SessionUtil.getFacesSessionObject("ListFilter"));
}
but when I set it back I lose the values in the filter field of DataTable.how should I do it ?
f:setPropertyActionListener in p:commandLink
How can I bring the data from the setter of the target- Attribut to the getter of the surrounding onclick-attribute?
I want to build an hyperlink within a p:CommandLink (onclick = "window.open('http://www.somewebsite.de ...')" for each row of the datatable. So the Request-parameters of "somewebsite" change depending on the actual row.
I have a working p:commandLink, because I can generate an new browser-window but only with a (constant! [and that is the problem/challenge]) website.
I can also access the needed data from EACH row through the p:commandLink that means the belonging data which I need for the hyperlink-Request-parameters, but I cannot bring it together, because I get a Nullpointer-Exception.
Look the spot which is marked with // HERE SEEMS TO BE THE PROBLEM
<p:dataTable value="#{columnsViewZwei.dataList1}" var="row"
resizableColumns="true"
styleClass="foo"
tableStyleClass="maggtabl2"
tableStyle="maggtabl2"
headerClass="order-table-header"
rowClasses="order-table-odd-row,order-table-even-row">
<p:column headerText="Titel" sortBy="#{row.titel}" filterBy="#{row.titel}">
<h:outputText value="#{row.titel}" />
</p:column>
<p:column headerText="Typ" sortBy="#{row.typ}" filterBy="#{row.typ}">
<h:outputText value="#{row.typ}" />
</p:column>
<p:column headerText="Metadaten" style="width:64px;text-align: center">
<p:commandLink value="show" title="show metatdata" onclick="#{columnsViewZwei.testurl}" >
<f:setPropertyActionListener value="#{row}" target="#{columnsViewZwei.selectedSET}" />
</p:commandLink>
</p:column>
</p:dataTable>
package bbb;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ManagedProperty;
import javax.faces.bean.ViewScoped;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.xml.stream.XMLStreamException;
import bbb.InspireDS;
#ManagedBean
#ViewScoped
public class ColumnsViewZwei implements Serializable {
private String testurl = "";
private InspireDS selectedDataset;
public InspireDS getSelectedDataset() {
System.out.println("getSelectedDataset() ");
return this.selectedDataset;
}
public String selectedDataset() {
selectedDataset = getSelectedDataset();
this.testurl = "window.open('..Info.do?fileIdentifier="+selectedDataset.metadaten+"')";
return testurl;
}
public void setTesturl(String testurl){
this.testurl = "window.open('http://...Info.do?fileIdentifier="+selectedDataset.metadaten+"')";
}
public String getTesturl() {
//selectedDataset = getSelectedDataset();
// HERE SEEMS TO BE THE PROBLEM: NullPointerException probably for selectedDataset
//this.testurl = "window.open('..?fileIdentifier="+selectedDataset.metadaten+"')";
this.testurl = "window.open(' ...')"; // works, but constant hyperlink
return testurl;
}
// This from f:setPropertyActionListener value="#{row}" target="#{columnsViewZwei.selectedSET}
// brings the data I need, but it should be accessable in getTesturl()
public void setSelectedSET(InspireDS inspirDS) {
this.selectedDataset = inspirDS;
this.testurl = "window.open('...do?fileIdentifier="+selectedDataset.metadaten+"')";
}
private List<InspireDS> dataList1;
#ManagedProperty("#{inspireTabelleBean}")
private InspireTabelleBean insTabBean;
#PostConstruct
public void init() {
try {
if(insTabBean==null){System.out.println("s e r v i ce = = n u ll");}
dataList1 = insTabBean.fillListbyparse();
} catch (XMLStreamException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void setService(InspireTabelleBean service) {
this.insTabBean = service;
}
public List<InspireDS> getDataList1() {
return dataList1;
}
public void setInsTabBean(InspireTabelleBean service) {
this.insTabBean = service;
}
}
I'm not able to reproduce your code, i did it a simplified version without datatable.
Instead of window.open function i simply used target="_blank" as explained in this this post.
The error you made is use onclick coupled with f:setPropertyActionListener. The reason why that doesn't function is explained in this post.
faces-redirect=true is needed for change url (read more on this post), and ajax must be false.
However, when you want call bean function through js, take a look to remote command.
xhtml Page:
<h:form id="form_open_window" target="_blank">
<p:commandLink value="show" title="show metatdata" action="#{columnsViewZwei.goToUrl()}" ajax="false" >
<f:setPropertyActionListener value="metadata_parameter" target="#{columnsViewZwei.metadata}" />
</p:commandLink>
</h:form>
Bean Code:
private String testurl = "index";
private String metadata;
public String getMetadata() {
return metadata;
}
public void setMetadata(String metadata) {
this.metadata = metadata;
}
public String goToUrl() {
System.out.println(metadata);
return testurl + "?faces-redirect=true&fileIdentifier=" + metadata;
}
PS: If You don't want deviate too much from your solution, evaluate use of oncomplete instead of onclick, but i didn't try it.
I hope this is useful.
and thank you for your fast answer. I used your suggestions, but what happend was not what i want.
(The table was distorted etc.)
I put in each Method (getter and setter) the System.out.println(...) as you did for goToUrl() {...},
but I did not see any printouts on the console. So it is mysterious which code was invoked.
When I clicked on the commandLink I got a new window with the same facelet "index.xhtml" as i started from. I guess this is the effect of testurl = "index"; My view-file (facelet) has the name "index.xhtml".
I think it is good that you mentioned the other posts so I can try to understand your arguments.
I suppose that I need the value="#{row}" in the f:setPropertyActionListener to access the current row/dataset because in this current row/dataset is the information which i need to build the url to call/access a remote website.
With kind regards,
Yours sincerely
tsitra
ooooooooooooooooo
date:22.12.2014
A possible solution without using a Primefaces component ( control ) was :
<p:dataTable value="#{columnsViewZwei.dataList1}" var="o9"
resizableColumns="true">
<p:column headerText="Titel" sortBy="#{o9.titel}" filterBy="#{o9.titel}">
<h:outputText value="#{o9.titel}" />
</p:column>
<p:column headerText="Typ" sortBy="#{o9.typ}" filterBy="#{o9.typ}">
<h:outputText value="#{o9.typ}" />
</p:column>
<p:column headerText="Metadaten">
show
</p:column>
</p:dataTable>
I have a dataTable with a list of items and a checkbox for selecting an item to edit. Ticking an item and clicking the edit button pops up a component which has fields and update and cancel buttons. Here's what happens.
Dialog appears
I empty all fields and click UPDATE, required messages appear since all fields are empty, data is not saved
Click CANCEL, dialog disappears
Click and edit the same item again
Some fields are not showing. I checked the datatable and database, data for the item still exists. Just not showing on the edit dialog the 2nd time around.
I noticed that the fields not showing are only those that have NULL attributes. NOT NULL fields are fine. I wonder if this has something to do with sessions. (Using Primefaces for all components)
Code for the edit dialog
<p:dialog header="#{bundle.Edit}" modal="true" widgetVar="editDialog" resizable="false">
<h:form id="edit-form">
<p:messages id="edit-error" autoUpdate="true" closable="true"/>
<h:outputLabel value="#{bundle.Name}" for="name" /><span class="required">*</span>
<p:inputText id="name" value="#{controller.selected.name}" required="true" requiredMessage="#{bundle.Name} #{bundle.FieldIsRequired}" maxlength="45"/>
<h:outputLabel value="#{bundle.Input}" for="input" /><span class="required">*</span>
<h:selectOneRadio id="input" value="#{controller.selected.input}" required="true" requiredMessage="#{bundle.Input} #{bundle.FieldIsRequired}">
<f:selectItem itemLabel="◯ " itemValue="0" />
<f:selectItem itemLabel="☓ " itemValue="1" />
</h:selectOneRadio>
<h:outputLabel value="#{bundle.IsOption}" for="isOption" /><span class="required">*</span>
<h:selectOneRadio id="isOption" value="#{controller.selected.isOption}" required="true" requiredMessage="#{bundle.IsOption} #{bundle.FieldIsRequired}">
<f:selectItem itemLabel="◯ " itemValue="0" />
<f:selectItem itemLabel="☓ " itemValue="1" />
</h:selectOneRadio>
<h:outputLabel value="#{bundle.Remark}" for="remark" />
<p:inputTextarea id="remark" value="#{controller.selected.remark}"/>
<p:commandButton action="#{controller.update()}"
value="#{bundle.Save}"
actionListener="#{controller.prepareList()}"
oncomplete="handleEditDialog(xhr, status, args)"
update=":form:datatable :edit-form:edit-error"
/>
<p:commandButton value="#{bundle.Cancel}"
onclick="editDialog.hide(); reset();"
type="button"/>
</h:form>
</p:dialog>
Code for the update function
public String update() {
RequestContext context = RequestContext.getCurrentInstance();
try {
current.setUpdateDate(new Date());
Map<String, Object> param = JsfUtil.getExternal().getSessionMap();
int createUser = (Integer) param.get("LOGIN_ID");
Account account = accountFacade.find(createUser);
current.setUpdateUser(account);
getFacade().edit(current);
search();
prepareList();
JsfUtil.addSuccessMessage(ResourceBundle.getBundle(JsfUtil.getSessionBundle()).getString("Updated"));
updateOk = true;
current = null;
context.addCallbackParam("updated", true);
return "";
} catch (Exception e) {
if (e.getCause().getCause().getMessage().contains("uk_")) {
JsfUtil.addErrorMessage("edit-form:edit-error",JsfUtil.getResourceString("Duplicate"));
context.addCallbackParam("updated", false);
} else {
JsfUtil.addErrorMessage(e, ResourceBundle.getBundle(JsfUtil.getSessionBundle()).getString("PersistenceErrorOccured"));
context.addCallbackParam("updated", false);
}
return null;
}
}
Based on what you are describing I think the following scenario is happening. When you see the form and you delete all fields and you hit save, the components that have no validators are actually overwriting some of the previous property values of your bean. That's why when you reload it for a second edit, you'll notice that all the components that have a validator attached (e.g required = true) are appearing but those with no validators are blank. In reality, you are actually doing a partial update. What you actually need to do is mark all fields as required to avoid that problem.
My teammate just found out a fix for this one. There is a function on the controller generated by NetBeans called prepareList().
public String prepareList() {
recreateModel();
return "";
}
private void recreateModel() {
items = null;
}
Basically we added this on catch block of the update() function. It is called after every unsuccessful update and recreates the model. The bug existed because the field retained its state of not having a value after an unsuccessful update.
I'm trying to use datatable component but using an array instead of an object inside selection property
Here is the sample from Primefaces showcase
My domain name is EfaDocLabelModel and I updated the CarDataModel(from showcase) to EfaDocLabelModel here is the code .
public EfaDocLabelModel() {
}
public EfaDocLabelModel(List<EfaDocLabelMatch> data) {
super(data);
}
#Override
public EfaDocLabelMatch getRowData(String rowKey) {
List<EfaDocLabelMatch> docLabelList = (List<EfaDocLabelMatch>) getWrappedData();
for (EfaDocLabelMatch docLabel : docLabelList) {
if (docLabel.getDoclabelId().toString().equals(rowKey)) {
return docLabel;
}
}
return null;
}
#Override
public Object getRowKey(EfaDocLabelMatch docLabel) {
return docLabel.getDoclabelId();
}
When user triggered commandbutton inside footer part of datatable ,in my managed bean
I try to get selected rows but it gives
java.lang.NullPointerException
at java.lang.reflect.Array.newArray(Native Method)
at java.lang.reflect.Array.newInstance(Array.java:70)
at org.primefaces.component.datatable.feature.SelectionFeature.decodeMultipleSelection(SelectionFeature.java:53)
at org.primefaces.component.datatable.feature.SelectionFeature.decode(SelectionFeature.java:39)
at org.primefaces.component.datatable.DataTableRenderer.decode(DataTableRenderer.java:53)
I can run the multiple selection from showcase which used CarDataModel but using with the same structure when I modify it to according my need I gat this error.
Here is my bean
public class DocLabelBean implements Serializable {
private List<EfaDocLabelMatch> docLabelMatchList;
private EfaDocLabelMatch[] docLabelArray;
public String updateLabel() {
EfaDocLabelMatch[] selectedDocArray = getDocLabelArray();//I m sure I m
//getting error because selectedDocArray is null.
DTO result = new DTO();
for (EfaDocLabelMatch docLabel : selectedDocArray) {
}
Here is my page necessary code:
<p:dataTable emptyMessage="No Labeled Doc"
var="docLabel" value="#{docLabelBean.docLabelModel}"
selection="#{docLabelBean.docLabelArray}">
<p:column selectionMode="multiple" />
<p:column headerText="Id" >
#{docLabel.doclabelId}
</p:column>
<p:column headerText="Doc No" >
#{docLabel.documentId}
</p:column>
<p:column headerText="Label Key" >
#{docLabel.docLabelKey}
</p:column>
And commanbutton code from page:
<p:commandButton value="Sil" icon="ui-icon-search"
update="#form" process="#form"
action="#{docLabelBean.updateLabel()}" />
I tried ajax=false and other possible error reason mentioned by balusc but still I couldn't resolve the problem
Here is
Bug report .I need to assign selected rows to an list or array if its a real bug is there any alternative ways ?
I've been googling this for 2 days now and trying various attempts that i've seen posted on the web, but nothing seems to be working for me.
I'm trying to get a richfaces 3 datatable to have sorted columns and when i click the column header, nothing actually gets sorted.
Anyone have any idea what i'm missing? Do i need to implement an attribute on my backing bean or something?
<rich:extendedDataTable id="resultsTable" value="#{tableBacking.results}" var="results" rowKeyVar="row">
<rich:column>
<f:facet name="header">
<h:outputText value="Row Number" />
</f:facet>
</rich:column>
<rich:columns value="#{tableBacking.columns == null ? '' : tableBacking.columns}"
var="columns" index="ind" id="column#{ind}"
sortBy="#{results[ind].data}" rendered="#{tableBacking.columns != null}">
<f:facet name="header">
<h:outputText value="#{columns.columnDescription}" />
</f:facet>
<h:outputText value="#{results[ind].data}" />
</rich:columns>
</rich:extendedDataTable>
TableLookupBacking bean
public class TableLookupBacking{
private List<List<TableData>> results = null;
private List<TableData> columns = new ArrayList<TableData>();
public void search() {
getData("");
}
private void getData(String whereClause) {
try {
DataDao dd = new DataDao();
results = dd.getData(WebDataViewerConstants.SCHEMA_NAME, selectedTable, whereClause);
} catch (Exception e) {
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage("Unable to retrieve data with selected search criteria in getData."));
}
}
// columns get set in another method that is triggered off something else the user does
// Getters and Setters
}
I finally figured it out. All i needed to do was to add sortOrder="#{tableBacking.sortOrder[columns]}" to my rich:columns tag and then in my backer just add the following:
private Map<String, Object> sortOrder = new HashMap<String, Object>();
// setter and getter