I have Search form. After clicking on Search button data refreshes, but dataTable columns not (columns get refreshed only on second button click). Apparently I am doing something wrong. Dynamic column code is inspired by PrimeFaces Showcase
myForm.xhtml:
<h:form id="MY_FORM">
#{myBean.initBean()}
...
<h:panelGrid id="main">
<h:panelGrid id="buttons">
<p:commandButton id="submitSearch"
value="#{msg['button.execute']}"
actionListener="#{myBean.submitSearch}"
update="resultPanel"/>
</h:panelGrid>
</h:panelGrid>
<h:panelGrid id="resultPanel" border="0">
<p:dataTable id="resultTable" var="result" value="#{myBean.searchResults}">
<p:columns value="#{myBean.columns}" var="column" columnIndexVar="colIndex">
<f:facet name="header">
<h:outputText value="#{column.header}" />
</f:facet>
<h:outputText value="#{result[column.property]}" />
</p:columns>
</p:dataTable>
</h:panelGrid>
</h:form>
I've called column calculation method createColumns() from myBean.initBean() and myBean.submitSearch() with the same result (I see it works correctly from debugger).
#ManagedBean("myBean")
#Scope(value = "session")
public class MyBean {
private ArrayList<HashMap<String,Object>> searchResults;
private List<ColumnModel> columns;
...
public void initBean() {
...
createColumns(selectedDates);
}
public void submitSearch() {
...
ArrayList<HashMap<String, Object>> results = prpRepository.getSearchResultByParams(searchParams);
createColumns(selectedDates);
}
private void createColumns(ArrayList<String> selectedDates){
columns = new ArrayList<ColumnModel>();
columns.add(new ColumnModel("Name", "NAME"));
columns.add(new ColumnModel("Amount", "AMOUNT"));
for (int i = 0; i < selectedDates.size(); i++) {
columns.add(new ColumnModel(selectedDates.get(i), "DATE" + i));
}
setColumns(columns);
}
public List<ColumnModel> getColumns() {
return columns;
}
public void setColumns(List<ColumnModel> columns) {
this.columns = columns;
}
}
Additional information:
I am using:
Oracle's Implementation of the JSF 2.1 Specification
JavaServer Pages API 2.2
Springframework 3.1.2 (incl. spring-context, spring-web)
Glassfish Javax.annotation 3.1.1
The problem is that columns is calculted only once time in the org.primefaces.component.datatable.DataTabe
public List<UIColumn> getColumns() {
// if(columns == null) {
columns = new ArrayList<UIColumn>();
FacesContext context = getFacesContext();
char separator = UINamingContainer.getSeparatorChar(context);
for(UIComponent child : this.getChildren()) {
if(child instanceof Column) {
columns.add((UIColumn) child);
}
else if(child instanceof Columns) {
Columns uiColumns = (Columns) child;
String uiColumnsClientId = uiColumns.getClientId(context);
uiColumns.updateModel(); /* missed code */
for(int i=0; i < uiColumns.getRowCount(); i++) {
DynamicColumn dynaColumn = new DynamicColumn(i, uiColumns);
dynaColumn.setColumnKey(uiColumnsClientId + separator + i);
columns.add(dynaColumn);
}
}
}
// }
return columns;
}
You could comment it and overwrite the class file,
or find the DataTable object and setColumns to NULL inside the createColumns method
Finally I've worked better with debugger and found an answer. Solution was to put createColumns() method call into columns getter getColumns() as it is called automatically before submitSearch(). My problem was in application lifecycle understanding.
Related
I could not find a way to make update of a component after getting filtered values of datatable. I try different ways :
Test 1 : I try to use the action of the Filter event, but this event is done before the change of values, so I can't make my calculation with the new values
Test 2 : I try to use the Listener of the Filter event, but I couldn't find the new values in the AjaxBehaviorEvent
Test 3 : I try to call my function in the setter of the filtered values, the calculation is done but not updated... So I try to use RequestContext of primefaces but it doesn't work either.
Tell me if you have an idea to unlock one way, or to propose new solutions =)
Thanks for your help!
Console return, to show the order of the event.
>test 1
>test 2
>update other
>update filtered values
>test 3
HTML code :
<h:form id="form1">
<p:dataTable var="_remontee" value="#{ConsultationSaisieBean.m_valuesInDataGrid}"
filteredValue="#{ConsultationSaisieBean.m_filteredValues}" >
<p:ajax event="filter" listener="#{ConsultationSaisieBean.OnFilter}"
action="#{ConsultationSaisieBean.FilterAction}" update=":form2"/>
<p:columns value="#{ConsultationSaisieBean.m_columns}" var="column"
filterBy="#{_remontee[column.property]}" filterMatchMode="contains">
<f:facet name="header">
<h:outputText value="#{column.header}" />
</f:facet>
<h:outputText value="#{_remontee[column.property]}" />
</p:columns>
</p:dataTable>
</h:form>
<h:form id="form2">
<p:outputLabel id="other" value="#{ConsultationSaisieBean.m_serviceResult}" />
</h:form>
Java class :
/**
* function of the filter event's action
*/
public void FilterAction()
{
// TEST 1
System.out.println("test 1");
setM_consummableResult(Resultof(m_filteredValues))
}
/**
* function of event's listener
*/
public Map<String, Object> OnFilter(AjaxBehaviorEvent p_event)
{
// TEST 2
System.out.println("test 2");
setM_consummableResult(Resultof(m_filteredValues))
// return values wanted by the event
DataTable table = (DataTable) p_event.getSource();
Map<String, Object> filters = table.getFilters();
return filters;
}
/**
* Setter of the filtered values
*/
public void setM_filteredValues(List<Map<String, String>> p_filteredValues)
{
// UPDATE FILTERED VALUES
System.out.println("update filtered values");
super.setM_filteredValues(p_filteredValues);
// TEST 3
System.out.println("test 3");
setM_consummableResult(Resultof(m_filteredValues))
//Test to force update
RequestContext context = RequestContext.getCurrentInstance();
context.addCallbackParam("saved", true);
context.update("form2:other");
}
/**
* function to count the new result
*/
public void Resultof(List<Map<String, String>> p_filteredValues)
{
/* calcul the new values */
}
/**
* getter for update the other component
*/
public Double getM_consummableResult()
{
return m_consummableResult;
// UPDATE
System.out.println("update other");
}
public List<Map<String, String>> getM_filteredValues()
{
return m_filteredValues;
}
private void setM_consummableResult(Double m_consummableResult)
{
this.m_consummableResult = m_consummableResult;
}
I've had the same problem as you. I have solved using remotecommand...
<p:ajax event="filter" oncomplete="handleLoadStart();" update=":frm1:tablaFact :frm1:panelTotal"/>
...
<p:remoteCommand id="rcom" name="loadRemoteContent" process="#this" update="panelTotal, tablaFact" actionListener="#{listadoFacturasMB.filterListener2}"/>
...
<h:outputScript id="waypointScript" target="body">
function handleLoadStart() {
loadRemoteContent();
}
</h:outputScript>
...
public void filterListener2() {
try {
if (filterFacturaUtilList != null) {
//then filter is active, do something...
}
} catch (Exception e) {
JsfUtil.addErrorMessage(e, "Error: filterListener() " + e.getMessage());
}
}
filterFacturaUtilList is a filteredValue
I'm having a hard time doing basic AJAX updates of a timeline.
Let me start with a basic example where I want to update the start and end times of a timeline based on the selection of a dropdown list:
<h:form id="form">
<h:outputLabel for="period" value="#{str.schedule_period}"/>
<h:selectOneMenu id="period" value="#{timelineController.period}" label="#{str.schedule_period}">
<f:selectItems value="#{timelineController.periodWeeks}" />
<p:ajax event="change" update="timeline" />
</h:selectOneMenu>
<pe:timeline id="timeline" value="#{timelineController.model}"
editable="true"
eventMargin="0"
minHeight="120"
stackEvents="false"
start="#{timelineController.timelineStart}"
min="#{timelineControllertimelineStart}"
end="#{timelineController.timelineEnd}"
max="#{timelineController.timelineEnd}"
showNavigation="false" showButtonNew="false"
showCurrentTime="false"
axisOnTop="true"
timeZone="#{timelineController.timezone}"
zoomMin="28800000"
dropActiveStyleClass="ui-state-highlight" dropHoverStyleClass="ui-state-hover">
<p:ajax event="drop" listener="#{timelineController.onDrop}"
global="false" process="timeline"/>
</pe:timeline>
</h:form>
When I select an item in the dropdown list, an AJAX event fires and sets the period property in the backing bean, but the new value is not reflected in the timeline component. As a workaround, I wrapped the timeline in a p:outputPanel and updated the wrapper instead and it works:
...
<h:selectOneMenu id="period" value="#{timelineController.period}" label="#{str.schedule_period}">
<f:selectItems value="#{timelineController.periodWeeks}" />
<p:ajax event="change" update="wrapper" />
</h:selectOneMenu>
...
<p:outputPanel id="wrapper">
<pe:timeline id="timeline" value="#{timelineController.model}"
editable="true"
eventMargin="0"
minHeight="120"
stackEvents="false"
start="#{timelineController.timelineStart}"
min="#{timelineControllertimelineStart}"
end="#{timelineController.timelineEnd}"
max="#{timelineController.timelineEnd}"
showNavigation="false" showButtonNew="false"
showCurrentTime="false"
axisOnTop="true"
timeZone="#{timelineController.timezone}"
zoomMin="28800000"
dropActiveStyleClass="ui-state-highlight" dropHoverStyleClass="ui-state-hover">
<p:ajax event="drop" listener="#{timelineController.onDrop}"
global="false" process="wrapper"/>
</pe:timeline>
</p:outputPanel>
Note that I also had to change the process attribute of p:ajax to wrapper.
So my first question is: why doesn't the update work without wrapping the timeline component?
My second question is about drag and drop. As you can you see from my code above, I have attached a drop listener to the timeline. And I'm also able to drag and drop events from a p:dataList BEFORE I make a selection in the dropdown list. Once I select a new period in the dropdown list, the timeline gets updated appropriately, but I'm not able to drag and drop events to the timeline any more (the onDrop listener doesn't get fired). Here's my p:dataList:
<p:dataList id="eventsList" value="#{timelineController.users}"
var="user" itemType="none">
<h:panelGroup id="eventBox" layout="box" style="z-index:9999; cursor:move;">
#{user.toString()}
</h:panelGroup>
<p:draggable for="eventBox" revert="true" helper="clone" cursor="move"/>
</p:dataList>
Any ideas what's wrong here?
I'm also including the TimelineController class for reference:
#ManagedBean
#ViewScoped
public class TimelineController {
#EJB UserService userDao;
private TimelineModel model;
private String name;
private ZoneId timezone;
private Period period;
private Duration defaultShiftDuration;
private LocalDateTime timelineStart;
private LocalDateTime timelineEnd;
#PostConstruct
protected void initialize() {
timezone = ZoneId.of("Europe/Berlin);
period = Period.ofWeeks(2);
defaultShiftDuration = Duration.ofHours(8);
timelineStart = LocalDateTime.now().with(DayOfWeek.MONDAY).withHour(0).withMinute(0).truncatedTo(ChronoUnit.MINUTES);
// create timeline model
model = new TimelineModel();
}
public TimelineModel getModel() {
return model;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getTimezone() {
return timezone.getId();
}
public void setTimezone(String timezone) {
this.timezone = ZoneId.of(timezone);
}
public List<SelectItem> getPeriodWeeks() {
List<SelectItem> weeks = Lists.newArrayList();
weeks.add(new SelectItem(1, "1 " + JsfUtil.getStringResource("schedule_week")));
weeks.add(new SelectItem(2, "2 " + JsfUtil.getStringResource("schedule_weeks")));
weeks.add(new SelectItem(3, "3 " + JsfUtil.getStringResource("schedule_weeks")));
return weeks;
}
public int getPeriod() {
return period.getDays() / 7;
}
public void setPeriod(int nWeeks) {
this.period = Period.ofWeeks(nWeeks);
timelineEnd = null;
}
public Date getTimelineStart() {
return Date.from(timelineStart.atZone(timezone).toInstant());
}
public Date getTimelineEnd() {
if (timelineEnd == null) {
timelineEnd = timelineStart.plus(period);
}
return Date.from(timelineEnd.atZone(timezone).toInstant());
}
public void setStartsOn(String startsOn) {
timelineStart = LocalDateTime.parse(startsOn + "T00:00");
timelineEnd = null;
}
public List<User> getUsers(){
return userDao.findAll();
}
public void onDrop(TimelineDragDropEvent e) {
// get dragged model object (event class) if draggable item is within a data iteration component,
// update event's start and end dates.
User user = (User) e.getData();
Date endDate = Date.from(e.getStartDate().toInstant().plus(defaultShiftDuration));
// create a timeline event (not editable)
TimelineEvent event = new TimelineEvent(user, e.getStartDate(), endDate, true, e.getGroup());
// add a new event
TimelineUpdater timelineUpdater = TimelineUpdater.getCurrentInstance(":form:timeline");
model.add(event, timelineUpdater);
}
}
The problem was a missing widgetVar attribute in the timeline component. This looks like a bug to me, since I'm not using the client side API of the component. I will file a bug in PF Extensions project.
I have one dataTable in my screen. In the column header there is a selectBooleanCheckBox. If I
select it then all the boolean check boxes are get selected and deselected if I deselect the header boolean check box. My datatable is binded is with htmlDatatable. In bean all values are being set but it is not reflected in UI. Kindly help....
<t:dataTable id="physicalAccessDetailsDataTable1" binding="#{bean.htmlDataTable}">
<f:facet name="header">
<h:selectBooleanCheckbox id="selectAll" value="#{bean.editablePhysical}"
valueChangeListener="#{bean.selectAllFromPhysicalDatatable}"
onclick="submit()" />
</f:facet>
here is my bean code...
public String selectAllFromPhysicalDatatable(ValueChangeEvent vcep) {
if (editablePhysical == false) {
System.out.println(editablePhysical);
editablePhysical = true;
for (PhysicalAccessDetail physicalAccessDetail : physicalAccessDetailsList) {
System.out.println("INside loop of true");
physicalAccessDetail.setEditable(true);
}
} else {
editablePhysical = false;
for (PhysicalAccessDetail physicalAccessDetail : physicalAccessDetailsList) {
physicalAccessDetail.setEditable(false);
}
}
return null;
}
I have been accessing the p:picklist for the first time and I am facing a issue where I am not able to get source and target values updated as in the p:picklist ui. I am using list of DualListModel<String>. Here is the code..
Please help me.
Thanks for the help!
code.xhtml
<p:dataTable value="#{updateSiteObj.dsList}" var="pickListObjDS" >
<p:column headerText="DS">
<p:pickList id="pojoPickListDSID" value="#{pickListObjDS}" var="ds" itemValue="#{ds}" itemLabel="#{ds}" showSourceFilter="true" showTargetFilter="true" filterMatchMode="contains" style="border-color: white!important" onTransfer="ajaxSubmit3()">
<f:facet name="sourceCaption">Available</f:facet>
<f:facet name="targetCaption">To be removed</f:facet>
</p:pickList>
<p:remoteCommand action="#{updateSiteObj.onDSTransfer}" name="ajaxSubmit3"/>
</p:column>
</p:dataTable>
UpdateSite.java
#ManagedBean(name = "updateSiteObj")
#SessionScoped
public class UpdateSite {
private List<DualListModel<String>> dsList = new ArrayList<DualListModel<String>>();
public List<DualListModel<String>> getDsList() {
return dsList;
}
public void setDsList(List<DualListModel<String>> dsList) {
this.dsList = dsList;
}
public String updateSiteDetails() {
ds.add(sg.getPrimaryDSID());
if (sg.getSecondaryDSID() != null) {
ds.add(sg.getSecondaryDSID());
}
System.out.print("DS:" + sg.getPrimaryDSID() + "=>" + sg.getSecondaryDSID());
DualListModel<String> tempDS = new DualListModel<String>();
tempDS.setSource(ds);
dsList.add(tempDS);
return "someSite?faces-redirect=true";
}
public void onDSTransfer() {
System.out.print("DSTransfer");
for (DualListModel<String> str1 : dsList) {
System.out.print("RemovedLBEntry:");
for (String dsName1 : str1.getTarget()) {
System.out.print("RemovedLB:" + dsName1);
}
}
}
}
When I try to call onDSTransfer after moving the values from source panel to target panel in picklist UI doesn't show any value from target.
Add update="#all" in p:remoteCommand.
You are doing a ajax call and not updating your UI.
<p:remoteCommand action="#{updateSiteObj.onDSTransfer}" name="ajaxSubmit3" update="#all"/>
In Primefaces 2.2 , if you want to get DataTable selection, you need to commit the datatable without validation failed or converter failed.
Like below:
<p:dataTable update="outputPanel" id="dataTable" var="car"
value="#{tableBean.cars}" selection="#{tableBean.selectedCars}">
<p:column selectionMode="multiple" />
<p:column style="width:200px" id="Model">
<f:facet name="header">
Model
</f:facet>
<h:outputText value="#{car.model}" />
<h:inputText value="#{car.model}" >
<f:attribute name="datatableClientId" value="form:dataTable" />
<f:attribute name="datatable" value="#{dataTableBinding}" />
<f:validator validatorId="dataTableRequiredValidator" />
</h:inputText>
</p:column>
</p:dataTable>
When the validator is falied, how to get the selectedCars in the validator?
I look into the Primefaces 2.2 source code , when decode a datatable will use the DataHelper class to decodeSelection. So I copy the code to write a util class ,like below:
//when the validator in a row, the datatable clientId will be wrong append the row number. so please specify the table clientId.
public Object getDataTableSelection(FacesContext context, DataTable table, String dataTableclientId) {
String clientId = dataTableclientId != null ? dataTableclientId : table.getClientId(context);
Map<String, String> params = context.getExternalContext().getRequestParameterMap();
String selection = params.get(clientId + "_selection");
Object data = null;
if (table.isSingleSelectionMode()) {
data = decodeSingleSelection(table, selection);
} else {
data = decodeMultipleSelection(table, selection);
}
table.setRowIndex(-1); // clean
return data;
}
private Object decodeSingleSelection(DataTable table, String selection) {
Object data = null;
if (isValueBlank(selection)) {
table.setSelection(null);
table.setEmptySelected(true);
} else {
int selectedRowIndex = Integer.parseInt(selection);
int first = table.getFirst();
int rows = table.getRows();
int last = rows == 0 ? table.getRowCount() : rows;
if (first <= selectedRowIndex && (first + last) > selectedRowIndex) {
table.setRowIndex(selectedRowIndex);
data = table.getRowData();
table.setSelection(table.getRowData());
}
}
return data;
}
private boolean isValueBlank(String value) {
if (value == null)
return true;
return value.trim().equals("");
}
private Object decodeMultipleSelection(DataTable table, String selection) {
Class<?> clazz =
table.getValueExpression("selection").getType(FacesContext.getCurrentInstance().getELContext());
Object data = null;
if (isValueBlank(selection)) {
data = Array.newInstance(clazz.getComponentType(), 0);
table.setSelection(data);
} else {
if (!table.isCellSelection()) {
String[] rowSelectValues = selection.split(",");
data = Array.newInstance(clazz.getComponentType(), rowSelectValues.length);
for (int i = 0; i < rowSelectValues.length; i++) {
table.setRowIndex(Integer.parseInt(rowSelectValues[i]));
Array.set(data, i, table.getRowData());
}
table.setSelection(data);
}
}
return data;
}
So you can use the getDataTableSelection(current facescontext instance, datatable instance, table clientId) method to get the selection.It will return a array object(never be null).
Note: when the validator in a row, the datatable clientId will be wrong append the row number. so please specify the table clientId.