Sorting of primefaces dataTable with p:columns and converter - jsf-2

I have a primefaces data table with dynamic colums:
<p:dataTable value="#{curSearch.getSearchResults()}" var="curSearchResult" sortMode="multiple" rowKey="#{curSearchResult.getUniqueId()}">
<p:columns value="#{curSearch.determinePrimaryPropertyNames()}" var="curPrimaryPropName" sortBy="#{curSearchResult[curPrimaryPropName].getValue()}">
...
</p:columns>
</p:dataTable>
In most cases the value of sortBy expressions fits (e. g. for a date or a string), but I have one special data type which must be translated. This means sortBy="#{curSearchResult[curPrimaryPropName].getValue()}" delivers a string which must be converted in a i18n string. This i18n string should be sorted then. Unfortunately the attribute sortBy throws an IndexOutOfBoundsExcpetion if the expression does not contain brackets []. My idea was to call a method and distinguish there between the "normal" values and the values which must be translated. So my 2nd idea is to define a converter for the value which has to be translated. But is converted value evaluated by primefaces? I guess not. Is there maybe another approach for this?

I'm using something like the following code snippet in a Lazy Loading data table and it's working fine.
<p:columns value="#{listController.ColumnModel}"
width="#{column.width}" var="column" field="#{column.field.fieldName}" sortBy="#{ticket[column.field.fieldName]}"
columnIndexVar="colIndex">
I'm storing the field name in the column model as you can see. In the backing bean method used for loading table data you will receive a list of sortmeta so you can sort your result set. I hope this can help you.

Related

How to display a 2d array of elements

I'd like to display 2d array of floats in a table on a xhtml page using JSF2 and I have no idea how to do that. I tried to find the answer in google but I couldn't. All examples were displaying classes objects and I was not able to make it working with the table.
The sitation is:
I have the array of some size - the size of the array depends on entered data:
float [][] variables = new float[size1][size2]
After user enters data and pressess button a method is called in the managed bean. The calculation begins and the table is filled with data.
Please tell me how can I display the array.
To achieve that you can use c:forEach tag to dinamically buid a h:panelGrid. Just save size2, which is the column number as a property and store all the input numbers in a normal java.util.List. Then you set that size to the h:panelGrid columns attribute and the component will split the rows for you. You can also style the content inside the c:forEach tag, bordering it in order to give it a table behaviour.
<h:form>
<h:panelGrid columns="#{bean.numberCount}">
<c:forEach var="num" items="#{bean.numberList}">
#{number}
</c:forEach>
</h:panelGrid>
</h:form>
EDITED
If you want to maintain the original structure but in a List, you can create a List<List<Float>>. That means a List which is composed by List which contains Float Objects. The same as a 2d array.
private List<List<Float>> _Matrix;
public List<List<Float>> get_Matrix() {
return this._Matrix;
}
/**
* Constructor for BackingBean.
*/
public BackingBean() {
this._Matrix = new ArrayList<List<Float>>();
this._Matrix.add(new ArrayList<Float>());
this._Matrix.add(new ArrayList<Float>());
this._Matrix.get(0).add(1.0f);
this._Matrix.get(0).add(2.0f);
this._Matrix.get(0).add(3.0f);
this._Matrix.get(1).add(1.0f);
this._Matrix.get(1).add(2.0f);
this._Matrix.get(1).add(3.0f);
}
In the code above I'm constructing the equivalent to a 2d array with 2 rows and values 1.0,2.0 and 3.0 on each row. You can use this code to iterate it over the view:
<h:panelGrid columns="#{backingBean._ColumnNumber}">
<c:forEach var="row" items="#{backingBean._Matrix}">
<c:forEach var="value" items="#{row}">
#{value}
</c:forEach>
</c:forEach>
</h:panelGrid>
Where #{backingBean._ColumnNumber} will be the length of the first array of the List (supposing all of them have the same length).
Good Luck.

Binding value of UISelectOne and UISelectMany to same property

I'm trying to create a dynamic survey application in Prime Faces. I have a list of Question Objects that each contains a list of AnswerChoices. These are given to the f:selectItems value attribute. This is fine. The question object also contains a List of selectedValues which is given to the relevant selectOne/many component.
Because I'm looking to be generic, there will be questions that have multiple selected values and also some that have only one selected value. I wanted to be able to point the selectOne and selectMany components to the List of strings within the relevant Question object that represents the selectedValues.
This works ok for the selectMany component, but not for the selectOne component which needs to be pointed at a singular object rather than a list. Is there an easy way around this that I'm missing - as I'd like to only have one object representing the selectedValues if possible
You can use brace notation to bind the value to a list/array item at a specific index. The below example binds the value to the 1st item of the list/array.
<h:selectOneMenu value="#{bean.selectedAnswers[0]}" />
There's however a caveat: you need to prepare the list/array with the single item yourself during bean's (post)construction. JSF/EL won't do that in case of a <h:selectOneMenu>.
E.g.
#PostConstruct
public void init() {
selectedAnswers = new ArrayList<Answer>();
selectedAnswers.add(null);
}
It doesn't harm to reuse this preinitializated property for UISelectMany components by the way.

Is it possible to call a setter for name in hidden tag in struts2?

In my jsp page I have a form (in struts2) in which I try to assign the size of the ArrayList data to the Vector testVector using hidden tag after submitting the form, but I still get the size of testVector equal to 0.
<s:hidden name="testVector.size" value="%{data.size}" />
I have created setters and getters for data and testVector in my Action class.
private Vector<String> testVector = new Vector<String>();
private ArrayList<String> data = new ArrayList<String>();
private String testName; // a field for testing the form.
// getters and setters of testVector and data
I supposed that this should work because Vector has a public setter setSize(int size).For the other field "testName" it was well submitted.. So am I have a problem in syntax?
Thank you a lot.
You should not be using a Vector. Be aware that OGNL does not work the same from a requests as it does when rendering a JSP for security reasons, it is simply too powerful. You should just set bean properties, collections, arrays and built in types from the request (type converters ignored).
From the request in this case it thinks are are trying to put values into the vector. I think it will create a new string put it into the Vector and then try to set the size of string (which will not work because that method does not exist).
To confirm this I tried to do what you are saying and the log states:
WARNING: Error setting expression 'testVector.size' with value '[Ljava.lang.String;#5c7b2d2f'
ognl.OgnlException: Error converting given String values for Collection. [ognl.NoSuchPropertyException: java.lang.String.size]
If you want this behaviour you will need to create a second method such as setSize() within your action which then goes about modifying the vector size. This I don't think is a very good thing to expose to the outside world, what if someone called your action many times passing in a huge number?

What is the correct code pattern to fetch data base entries?

In two other questions (here and here) BalusC makes a straight up declaration:
Getters are solely there to access bean properties, not to do some business logic. There you have the bean constructor, initialization blocks or event methods for. All are executed only once during bean's life and that's exactly what you want.
Well gee -- this just invalidated a gazillion lines of code I have already written. Ok, then, what is the correct way of implementing a backing bean that fills a data table? I understand his point and the concept, but not the practice. My question is twofold:
Why is the way I am doing it wrong?
How do I fix it?
I use PrimeFaces p:dataTable a lot, and it's value attribute resolves to a collection. For reasons I don't go into here, I do not use PrimeFaces' lazy table loading feature. Instead I implement my own filter/sort controls, but they trigger AJAX events, which then results in the table being filled with records fetched from the data base.
The table is marked up like this:
<p:panel id="mqTable">
<h:outputText value="Sort/Filter: #{maintCategory.tableQueryParameters}" />
<p:dataTable
id="mqDataTable"
rows="#{maintCategory.pageSize}"
value="#{maintCategory.dataModel}"
selection="#{maintCategory.selected}"
var="cat"
selectionMode="single"
emptyMessage="No Categories Found">
Now the INCREDIBLY BAD UN-JSFish (or so I just found out) getter for dataModel goes like this:
public ATMDataModel getDataModel() {
TableQueryParameters p = getTableQueryParameters();
if (p.isChangePending()) clearDataModel();
p.setChangePending(false);
if (dataModel != null) return dataModel;
List<ET> list = getDAO().runQuery(p);
if (p.isNeedResultSize()) p.setResultSize(getDAO().runQueryCount(p));
dataModel = new ATMDataModel(list);
return dataModel;
}
A few explanations.
This is from an abstract super-class where ET is the "Entity Type." All my CRUDs use this same routine.
The class ATMDataModel is a wrapper for the list which implements SelectableListModel. The row selection logic in PrimeFaces requires this. (It is a pain that appeared in PF 3 but it makes row selection work more reliably.)
The class TableQueryParameters is something I wrote to encapsulate the current state of the table on the user's screen. It includes what sort parameters, what filter parameters, what page we are on, etc. Because this needs to be preserved, the backing bean is ViewAccesScoped (via MyFaces CODI) and the TableQueryParameters is a property within it.
The TableQueryParameters are updated in response via AJAX events, which also update the form causing getDataModel to be called. The method isChangePending goes true when anything changes. So the getDataModel method uses this to generate only one fetch from the DAO between changes no matter how many times it is called.
BUT if the TableQueryParameters do change, I have to call runQuery with those parameters to fetch the new set of records the user wants to see. If I don't call it in getDataModel where do I call it?
Please advise.
You're basically lazily loading the data in the getter. You're not hitting the DB on every getter call within the same request (or view) scope. This is affordable. I don't use CODI, but I'd imagine that the getTableQueryParameters() call is also particularly cheap and nothing to worry about.
As to the concrete question, you'd normally do the DB/business job in an action(listener) method which is attached to the UICommand component and/or the ajax event tag.
For example (works also as <p:commandButton action> though)
<p:ajax listener="#{bean.deleteSelectedRow}" />
...
<p:ajax listener="#{bean.saveRowDetail}" />
with
public void deleteSelectedRow() {
someService.delete(selectedRow);
dataModel = loadDataModel();
}
public void saveRowDetail() {
someService.save(selectedRow);
dataModel = loadDataModel();
}
Depending on the meaning of p.isChangePending(), I think you could also get rid of it this way, it look like that you were setting it in the action(listener) methods.

Using OGNL to return data from a Map<String,Object>

Using Struts 2.1.6, xwork 2.1.2 and ognl 2.6.11
In my struts action I have a Map that I am fetching elements from using OGNL. If the key I am using to fetch with does not exist in the map then OGNL returns an empty object array, that OGNL converts to a string and I get the object reference java.lang.Object#6.... This occurs in several places and seems to be the the map having the value generic specified as an Object. This is not something I can change.
I have traced the problem for a while, but when I ended up deep into the guts of the OGNL code and I did not see a light at the end of the tunnel. Currently I am going to go with an ugly hack of checking the string return to see if it starts with "java.lang.Object#" and if so return an empty string. I don't like the solution but thats what time permits.
Has anyone encountered a similar problem?
Also, where did OpenSymphony go? updates to their webiste appear to have dried up, the user forums say they are being converted to Google Groups no later than Nov-12-09
This is a problem with null values: if value is null, default behavior is to create a value using default constructor. Since the value type of your map is Object, new Objects are created where null is.
In order to stop this behavior:
use #CreateIfNull( value = false )
use mapName_CreateIfNull=false in classname-convertion.properties file.

Resources