I am having a two level map Map<String,HashMap<String,String>> which i need to display using a <p:dataTable>. The code of managed bean is as follows:
#ManagedBean(name="MyBean")
public class MyBean{
private Map<String,HashMap<String,String>> twoDimentionalMap;
public void getMapData(){
twoDimentionalMap=getDataFromDataStore();
}
}
Now I am using this map in my view.xhtml file as follows:
<p:dataTable var="entrySet1" value="#{MyBean.twoDimentionalMap.entrySet()}">
<p:columns var="entrySet2" value="#{entrySet1.getValue()}">
#{entrySet2.getKey()} - #{entrySet2.getValue()}
</p:columns>
</p:dataTable>
I also tried using
<p:dataTable var="entrySet1" value="#{MyBean.twoDimentionalMap.entrySet()}">
<p:columns var="entrySet2" value="#{MyBean.twoDimentionalMap.get(entrySet1.getKey()).getValue()}">
#{entrySet2.getKey()} - #{entrySet2.getValue()}
</p:columns>
</p:dataTable>
I even tried converting the outer map to a list:
List<HashMap<String,String>> twoDimentionalMap;
However nothing is displayed on datatable. The execution shows no error but there is nothing displayed on the page.
Kindly suggest if I am doing something wrong or if <p:columns> is having any issue handling maps.
Thanks
since the keys of outer map do not have a meaning, converting the outer map to a list is correct.
but your approach to retrieve column names from xhtml does not seem valid. you need to get them independently from the current iteration variable entrySet1, otherwise you add a third dimension to the operation, which data table cannot handle.
we need to assume that all keys are same across the listed maps.
here is the code for xhtml:
<p:dataTable var="entrySet1" value="#{testMB.twoDimensionalMap}">
<p:columns var="keySet2" value="#{testMB.columnNames}">
#{keySet2} - #{entrySet1[keySet2]}
</p:columns>
</p:dataTable>
and for the bean:
#Named
#ViewScoped
public class TestMB implements Serializable {
private List<HashMap<String,String>> twoDimensionalMap;
public TestMB()
{
getMapData();
}
private void getMapData(){
//twoDimentionalMap=getDataFromDataStore();
twoDimensionalMap = new ArrayList<HashMap<String,String>>();
twoDimensionalMap.add(new HashMap<String,String>());
twoDimensionalMap.get(0).put("key0", "value00");
twoDimensionalMap.get(0).put("key1", "value01");
twoDimensionalMap.add(new HashMap<String,String>());
twoDimensionalMap.get(1).put("key0", "value10");
twoDimensionalMap.get(1).put("key1", "value11");
}
public Set<String> getColumnNames()
{
return twoDimensionalMap.size() > 0 ? twoDimensionalMap.get(0).keySet() : new HashSet<String>();
}
public List<HashMap<String, String>> getTwoDimensionalMap() {
return twoDimensionalMap;
}
}
Related
I'm developing a JSF Application with PrimeFaces.
For a Picklist I'm trying to use the omnifaces.ListConverter but the list property in this converter is not set.
reporting-edit.xhtml
<p:pickList id="picklist_columns" value="#{reportingEditView.columns}" var="repcolumn"
itemLabel="#{repcolumn.column.name}" itemValue="#{repcolumn}" responsive="true"
showSourceFilter="true" filterMatchMode="contains">
<o:converter converterId="benni.ListConverter" list="#{reportingEditView.columns.source}"/>
<f:facet name="targetCaption">Spalten</f:facet>
<p:ajax event="transfer" listener="#{reportingEditView.handleColumnTransfer}"/>
reportingEditView.java
#ManagedBean
#SessionScope
public class ReportingEditView
{
private DualListModel<RepColumn> columns;
public void initPickList()
{
List<RepColumn> availableColumns = new ArrayList<>();
List<RepColumn> selectedColumns = new ArrayList<>();
... populate availableColumns and selectedColumns ...
this.columns = new DualListModel<RepColumn>();
this.columns.setSource(availableColumns);
this.columns.setTarget(selectedColumns);
}
}
All the columns are displayed in my PickList but when I put one column from the source list into the target list a NullPointer is thrown because the list in the converter is not set.
Also the listener Method is not called when transferring an element in the PickList.
Any ideas what I am doing wrong?
Shame on me!
I fixed that issue. I don't know how but there was xmlns:o="http://java.sun.com/jsf/core" in my reporting-edit.xhtml. After replacing that with xmlns:o="http://omnifaces.org/ui" everything works like a charm.
i have a datatable with dynamic columns, defined by a columnModel. The String property links to the correct field (used for value output). The sort String equals the property, but some columns should not be sortable, sort is null or emtpy (tried both) there:
public class ColumnModel {
private String property;
private String sort;
private int width;
//GETTER
...
}
I use a List of that models to create my dynamic columns. The use of the width is working well:
<p:dataTable value="#{bean.items}" var="item" ... >
<p:columns value="#{bean.columnModel}" var="column" sortBy="#{column.sort}" width="#{column.width}">
...
</p:columns>
</p:dataTable>
My Question:
sortBy does not allow a null or an emtpy value. Otherwise i get a parse exception where it says, it cannot parse #{item.}. primefaces seems to add 'item' (my var of the datatable) automatically before the given sortfield.
How can some columns be ignored?
Thanks for your answeres!
Using primefaces 5.0.9 with Wildfly 9.0.2
Primefaces added new attributes for p:column(s) in version 5.1.3 and 5.2.0:
sortable
filterable
Here is the link to the solved Issue:
https://code.google.com/archive/p/primefaces/issues/5021
Example depending on my code above:
public class ColumnModel {
private String property;
private boolean sortable;
private int width;
//GETTER
...
}
With a given List<ColumnModel> columnModel in bean:
<p:dataTable value="#{bean.items}" var="item" ... >
<p:columns value="#{bean.columnModel}" var="column" sortBy="#{item[column.property]}" field="#{column.property}" sortable="#{column.sortable}" width="#{column.width}">
...
</p:columns>
</p:dataTable>
I'm currently using filters with options list on my lazy loaded DataTables. Everything works just fine except I'd like to be able to reload my filter options depending on currently selected filters.
For example, my DataTable has two fields country and region, and I want to filter
DataTable snippet :
<p:dataTable var="d" widgetVar="personneContactTable" value="#{bean.dataModel}" id="myDataTable" lazy="true">
<p:column sortBy="country" filterBy="country" filterOptions="#{bean.getCountryOptions()}">
[...]
</p:column>
<p:column sortBy="region" filterBy="region" filterOptions="#{bean.getRegionOptions()}">
[...]
</p:column>
</p:dataTable>
And my bean :
#ManagedBean(name = "bean")
#SessionScoped
public class MyBean implements Serializable {
LazyDataModel<MyStuff> dataModel;
String country;
String region;
public SelectItem[] getCountryOptions() {
return service.someMagic();
}
public SelectItem[] getRegionOptions() {
return service.someMoreMagic(country);
}
// + getters, setters, etc.
}
I tried using properties instead of methods, adding <p:ajax event="filter"> tags to try reloading part of the DataTable, but nothing worked. I found http://forum.primefaces.org/viewtopic.php?f=3&t=38087 too, but again not workable solution emerged.
How can I automaticaly refresh my filterOptions? (this is using Primefaces 4 ELITE branch)
On the Dashboard each User can see some Basic Stats. Lets take for example the "last login" Date. (But there are many more stats / values / settings to display)
The XHTML Files looks simplidfied like this:
<h:outputText value="statisticController.lastLoginDate()" />
The Bean itself uses #Inject to get the Session and therefore the current user:
#Named
#RequestScoped
public StatisticController{
#Inject
private mySessionBean mySession;
#PostConstruct
private void init(){
//load stats for mySession.currentUser;
}
}
Now, i want to generate a List where for example a certain role can view the values for ALL users. Therefore i can't use the Session Inject anymore, because the StatisticController now needs to be generated for multiple Users.
Having regular Classes this would not be a big problem - add the userEntity to the constructor. What is the "best practice" to solve this in JSF?
If i modify the StatisticController to something like this:
#Named
#RequestScoped
public StatisticController{
public void init(User user){
//load stats for user;
}
}
i would need to call init(user) manually of course. How can this be achieved from within a Iteration in the XHTML file?
I could refactor it so the valueLoading happens in the actual getter method, and iterate like this:
<ui:repeat var="user" value="#{userDataService.getAllUsers()}">
<h:outputText value="statisticController.lastLoginDate(user)" />
...
</ui:repeat>
But then i would need to load "every" value seperate, which is bad.
So a way like this would be "better":
<ui:repeat var="user" value="#{userDataService.getAllUsers()}">
statisticController.init(user);
<h:outputText value="statisticController.lastLoginDate()" />
...
</ui:repeat>
However this doesnt look very comfortable either. Further more doing things like this, will move nearly "all" Backend Stuff into the Render Response Phase, which is feeling wrong.
Any Ideas / Tipps how to solve this in a way that's not feeling "like a workaround"?
Create a new model wrapping those models.
public class UserStatistics {
private User user;
private Statistics statistics;
// ...
}
So that you can just use e.g.
public class UserStatisticsBacking {
private List<UserStatistics> list;
#EJB
private UserService userService;
#EJB
private StatisticsService statisticsService;
#PostConstruct
public void init() {
list = new ArrayList<UserStatistics>();
for (User user : userService.list()) {
list.add(new UserStatistics(user, statisticsService.get(user)));
}
}
// ...
}
(better would be to perform it in a new UserStatisticsService though)
with
<ui:repeat value="#{userStatisticsBacking.list}" var="userStatistics">
<h:outputText value="#{userStatistics.user.name}" />
<h:outputText value="#{userStatistics.statistics.lastLoginDate}" />
...
</ui:repeat>
An alternative to using a wrapped model proposed by BalusC is to store two separate lists with model data. With this approach you don't need to introduce modifications.
Following this route you'll be iterating over one list with <ui:repeat> and, ensuring equality of sizes of both lists, get second list element by index, which in turn is available via varStatus attribute that exports the iteration status variable.
<ui:param name="stats" value="#{bean.stats}/>
<ui:repeat value="#{bean.users}" var="user" varStatus="status">
<h:outputText value="#{user}/>
<h:outputText value="#{stats[status.index]}/>
</ui:include>
Population of lists may be done beforehand in PostConstruct method:
private List<User> users;
private List<Statistic> stats;
#PostConstruct
public void init() {
users = userService.list();
stats = statService.list();
}
I have a problem with selecting rows in the Primefaces Datatable. I use dynamic columns, so the standard row selection mechanism is not usable here, I implement checkbox selection myself.
To help, here's s simplified version of what I have in my xhtml:
<h:form>
<p:dataTable id="table"
var="result"
value="#{tableBean.results}">
<p:columns value="#{tableBean.columnNames}" var="column" columnIndexVar="colIndex">
<f:facet name="header">
#{column}
</f:facet>
<h:panelGroup rendered="#{colIndex==0}">
<h:outputLabel>#{rowIndex}</h:outputLabel>
<h:selectBooleanCheckbox value="#{tableBean.selectedRows[result[0]]}"/>
</h:panelGroup>
</p:columns>
</p:dataTable>
<h:commandButton value="Submit"></h:commandButton>
</h:form>
And here's what I have in the managed bean to select the checkboxes:
package testpackage;
import java.util.*;
import javax.faces.bean.*;
#ManagedBean
#SessionScoped
public class TableBean
{
private Map<String, Boolean> selectedRows = new HashMap<String, Boolean>();
List<List<String>> results = new LinkedList<List<String>>();
public TableBean()
{
List<String> row1 = new LinkedList<String>();
List<String> row2 = new LinkedList<String>();
row1.add("row1.ref");
row1.add("row1.id");
row1.add("row1.status");
row2.add("row2.ref");
row2.add("row2.id");
row2.add("row2.status");
results.add(row1);
results.add(row2);
//selectedRows.put("row2.ref", true);
}
public Map<String, Boolean> getSelectedRows()
{
return selectedRows;
}
public String submit()
{
List<List<String>> selectedResults = new ArrayList<List<String>>();
for (List<String> result : results)
{
if (selectedRows.get(result.get(0)) != null)
{
selectedResults.add(result);
selectedRows.remove(result.get(0));
}
}
return null;
}
public List<List<String>> getResults()
{
return results;
}
public List<String> getColumnNames()
{
List<String> columnNames = new LinkedList<String>();
columnNames.add("");
columnNames.add("REF");
columnNames.add("ID");
columnNames.add("STATUS");
return columnNames;
}
}
The getSelectedRows method works great, but the problem is that the setSelectedRows method is never called, so I don't know which checkboxes the user has selected. Maybe I overlook something very trivial, but cannot find the solution.
Any ideas on this? I would be very glad if you helped, or give any other row selection solution for the dynamic columns.
Thx in advance,
Levi
To me it looks you are rendering the wrong field in selectBooleanCheckBox.
You should be using variable or field from the result variable.
My solution:
In your situation you are rendering an object from List as a form of table row so if you want to make some changes and retrieve the status of that row then you should be using the variable from that object only.
I understand you are submitting the whole form and want to pickup all updated rows, in that case you will have to loop through the whole List and find all the rows which have been updated by checking the status in Request Handler(Action) bean.
Hope that helps.
The setter is never called for nested objects. You're the one who's responsible for creating them, not JSF. JSF just gets the nested object and then calls the setter on it (which is the put() method in case of a Map). You just need to determine the selected rows in the action method. Add an action method to the commandbutton:
<h:commandButton value="Submit" action="#{bean.submit}"></h:commandButton>
which is definied like follows (guessing/assuming that var="result" is in essence an Object[]):
public String submit() {
List<Object[]> selectedResults = new ArrayList<Object[]>();
for (Object[] result : results) {
if (selectedRows.get((String) result[0])) {
selectedResults.add(result);
selectedRows.remove(result[0]); // Reset.
}
}
// Now selectedResults contains all selected results.
}