All the questions asking: how can I bind POJOs to h:selectXX with f:selectItems end up with answer "use a converter". However, it seems it is possible to go without the converter - see:
Facelet:
<h:selectManyListbox value="#{pojoBean.selected}">
<f:selectItems value="#{pojoBean.allItems}" var="i" itemValue="#{i}" itemLabel="#{i.txt}" />
</h:selectManyListbox>
Bean:
public class PojoBean {
List<MyItem> selected;
List<MyItem> allItems;
POJO:
public class MyItem {
private String txt;
...}
Note that this seems to work only with h:selectManyListbox, when the value/s being selected end up in a list, not in a single property.
Question - why doesn't it work with h:selectOneMenu and etc?
Likely your MyItem class has already the toString() overridden which returns the txt and you were plain printing selected as follows to determine the selected values:
System.out.println(selected);
Try to cast each item of selected back to MyItem:
for (MyItem myItem : selected) {
System.out.println(myItem);
}
You'll see that it fails with ClassCastException, because it's actually a String. So yes, you still need a converter here.
See also:
How do I get selectManyCheckBox to return something other than List<String>?
Related
I'm converting from RichFaces to PrimeFaces 11.0 and replacing a custom component based on the old rich:suggestionBox with p:autoComplete. This works perfectly in most code where the value refers to a POJO property. However I'm finding difficulty getting p:autoComplete to set the value of a reference from an ArrayList of POJOs.
class OrderItem {
...
public List<Contact> getContacts() {
return contacts;
}
...
}
On a view, we want to be able to allow autoCompletes on multiple contacts per orderItem. The orderItem.contacts list is prepopulated with null placeholders by the time this is rendered:
<ui:repeat id="assignabilityContacts" value="#{orderItem.contacts}" var="_contact" varStatus="idx">
...
<p:autoComplete value="#{orderItem.contacts[idx.index]}"
id="existingContact"
binding="#{contactGroupManager.contactAutoComplete}"
converter="#{baseEntityConverter}"
completeMethod="#{contactGroupManager.autocomplete}"
forceSelection="true"
var="row" itemValue="#{row}" itemLabel="#{row.toString()}"
minQueryLength="2"
maxResults="10"
>
<f:attribute name="orderItem" value="#{orderItem}" />
</p:autoComplete>
The signature of the complete method is...
public List<Contact> autocomplete(Object suggest) {
}
And the converter follows Bauke Scholtz pattern for entities:
Implement converters for entities with Java Generics
As defined above, the value of orderItem.contacts[idx.index] is being set to a string, corresponding to the toString() of the selected pojo. The converter is never called.
If I change the value to a property of class Contact on another backing bean, the converter is called and the property is set to the selected Contact entity. This is the correct behavior.
value="#{contactSelector.selectedContact}"
Is it possible that autoConverter is not picking up the entity class from the List, and are there any suggested workarounds for this?
I'm using Primefaces version 5.3 autocomplete in a web project, I have written the search method and converter for the java entities i am searching for. These all work well the selected entity from the autocomplete is set correctly in the backing bean using a p:ajax tab and initially the entity name, which i specify in the itemValue, is set in the text input.
When i then submit the form that includes this autocomplete the variable in the backing bean which i set from the autocomplete stays as it is which is intended but the text input on the autocomplete will display the entities id instead of the name as i'd specified on the itemValue. This is because it is calling the toString method in my converter but I want it still to display the name yet i need the converter..
Has anyone come across this issue that may be able to help?
I have found a thread else where that describe this behaviour but it is a couple years old now and doesn't have an answer.
This thread is:
http://forum.primefaces.org/viewtopic.php?f=8&t=37918
As it may explain it better than i have...
Any help is appreciated.
UPDATE: code added
Here is my autocomplete tag, complete method returns a java List of entity type.
<h:form>
<p:autoComplete id="autocomplete" value="#{bean.selectedEntity}"
completeMethod="#{bean.listOfPossibleEntities}"
itemValue="#{_e}" itemLabel="#{_e.name}" autocomplete="off"
minQueryLength="3" var="_e"
placeholder="Enter Entity Name Here"
converter="EntityConverter" forceSelection="true">
<p:ajax event="itemSelect" update="enclosingForm"/>
<p:column>
<h:outputText value="#{_e.name}" />
</p:column>
</p:autoComplete>
<p:commandButton update="#form" >
</h:form>
FacesConverter looks like as below, i use a DAO call to our database find the object for each id
private EntityDAO entityDAO = (EntityDAO)Component.getInstance("entityDAO");
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
Integer id = Integer.valueOf(value);
return entityDAO.findById(id,false);
}
#Override
public String getAsString(FacesContext fc, UIComponent uic, Object object) {
String result = "";
if(object != null) {
if(object instanceof Entity){
result = ""+ String.valueOf(((Entity) object).getEntityId());
}
}
return result;
}
So yes on update of the h:form after the entity on the autocomplete is selected and the submitting with the p:commandButton the value display or the entity will change from the entity name to the entity id.
Hope this helps further thanks.
I'm trying to implement a <p:selectManyCheckbox> but I'm having no success.
Now I have the following architecture:
Course - have many Disciplines
Discipline - belongs to none, one or many Courses.
In Course class I have two ArrayList<Discipline>:
public class CourseMBean{
(...)
// Stores all disciplines
private static ArrayList<Discipline> allDisciplines;
// Stores only the disciplines that's already associated with this course.
private static ArrayList<Discipline> courseDisciplines;
(get and set for the arraylists)
(...)
}
All data comes from a MYSQL DB, but that isn't the question. Now I want create a new Course, so I don't have anything in courseDisciplines.
I want show allDisciplines in checkboxes, and want that when user select one checkbox, the object Discipline of this checkbox be added in courseDisciplines - and when unselect one checkbox, remove the discipline from the courseDsiciplines.
My JSF 2.0 code is following:
<p:selectManyCheckbox id="disciplines" value="#{courseMBean.allDisciplines}" layout="grid" columns="2">
<f:selectItems value="#{courseMBean.courseDisciplines}" />
</p:selectManyCheckbox>
This actually shows all disciplines without any selected checkboxes, what's right. But when I select some checkboxes and submit the form I try to print the elements inside courseDisciplines, and this don't show anything in console.
What I'm doing wrong?
when I select some checkboxes and submit the form I try to print the elements inside courseDisciplines
As the courseDisciplines actually represents the available items not the selected items, it seems that you misunderstood some basic concepts around the UISelectMany and UISelectItem(s) components.
The <f:selectItem(s)> (from the UISelectItem(s) family) represent the available items. It are exactly those items which are shown in the UI and which the enduser has to choose from.
The value attribute of <p:selectManyCheckbox> (from the UISelectMany family, like <h:selectManyCheckbox> and <h:selectManyMenu>) represent the (pre)selected items. If this is null or empty during first display of the form, then nothing is preselected. Or if this contains some preselected items, then only those available items which are equal() will be preselected.
When the enduser has changed the selection in the UI and submits the form, then all selected items will end up in the value attribute of UISelectMany component. The UISelectItem(s) remains unchanged.
Here's a basic kickoff example:
<p:selectManyCheckbox value="#{bean.selectedItems}">
<f:selectItems value="#{bean.availableItems}" />
</p:selectManyCheckbox>
<p:commandButton value="Submit" action="#{bean.submit}" />
<p:messages autoUpdate="true" />
private List<String> selectedItems; // +getter +setter
private List<String> availableItems; // +getter (no setter necessary!)
#PostConstruct
public void init() {
availableItems = new ArrayList<String>();
availableItems.add("one");
availableItems.add("two");
availableItems.add("three");
}
public void submit() {
System.out.println("Selected items: " + selectedItems);
}
(all other <p:selectManyXxx> and <h:selectManyXxx> components work exactly the same)
When a complex Javabean object like Discipline comes into the picture, then you need to make sure that there's a Converter for that so that JSF can properly convert between it and String for usage in generated HTML output and as HTTP request parameter (HTML and HTTP namely can't pass around nor hold Java objects, but only character sequences which are in Java represented by String).
This is perhaps your root problem. You said that nothing is printed to the console on submit. But it could be as good the case that the whole submit() method is actually never being invoked. You're not explicit enough on this. If the whole action method is indeed never invoked (i.e. a debug breakpoint doesn't hit there, or another System.out.println() printing a static string is never shown in console), then you've actually most likely a conversion error. If you have used <h|p:message(s)> the right way, or have paid attention to server log about queued but undisplayed faces messages, then you should have noticed it.
In that case, you need to implement a Converter which converts between Discipline and String.
#FacesConverter(forClass=Discipline.class)
public class DisciplineConverter implements Converter {
#Override
public Object getAsObject(FacesContext context, UIComponent component, String submittedValue) {
// Write code here to convert from String to Discipline.
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object modelValue) {
// Write code here to convert from Discipline to String.
}
}
More than often the DB ID is being used as String representation. See also the section "Complex object as selected item" of this answer on a related question: How to populate options of h:selectOneMenu from database?
there is a selectOneMenu in my example with a f:selectItems-attribute. The select-items are resolved from my bean like this:
<h:selectOneMenu value="#{bean.value}">
<f:selectItems value="#{bean.selectItems}" var="obj" itemValue="#{obj}" itemLabel="#{obj.name}"/>
</h:selectOneMenu>
The method getSelectItems() in my bean looks like that:
public List<MyObject> getSelectItems() {
List<MyObject> list = new LinkedList<MyObject>();
MyObject obj = new MyObject("Peter");
list.add(obj);
return list;
}
The objects that are displayed are simple objects with a attribute "name".
Nothing special up to this point. But now i change my method to that:
public List<MyObject> getSelectItems() {
List<MyObject> list = new LinkedList<MyObject>();
MyObject obj = new MyObject("<script>alert('xss is bad');</script>");
list.add(obj);
return list;
}
The javascript doesn´t get escaped by MenuRenderer-Class and my page shows me the alert-message.
Is there any cause why the default value of the escape-attribute of SelectItem is "false"?
How can i fix that problem? (I use Mojarra 2.1.7)
The default should indeed not have been false. I've reported it as issue 2747.
In the meanwhile, add itemLabelEscaped="true" to escape it anyway.
<f:selectItems ... itemLabelEscaped="true" />
Note that this is only necessary when you're using GenericObjectSelectItems, i.e. when you're supplying a E[]/List<E>/Map<K, V> instead of List<SelectItem>/SelectItem[]. Also note that escaping is only absolutely mandatory when it concerns user-controlled input (which is fortunately very rarely the case in dropdown values).
I have a datatable where I want to change the color of a cell based on some analysis that is run on the contents. The table is linked to an array of Comment objects, which I have given a String cssClass that gets updated once the analysis is run. This is what I have tried plugging into the rowClasses property of the datatable. It's not working and I think the issue may be that I cannot access the variable created for each row of the datatable, from inside the datatable declaration.
Datatable code:
<h:dataTable value="#{post.comments}" var="comment" class="hs-table" rowClasses="#{comment.cssClass}" >
<h:column>
#{comment.name}
</h:column>
<h:column>
#{comment.email}
</h:column>
<h:column>
#{comment.msg}
</h:column>
</h:dataTable>
The Comment class:
public class Comment {
private String msg;
private String email;
private String name;
private Date date;
private String cssClass;
public Comment(){
cssClass = "normColumn";
}
epublic String getCssClass() {
return cssClass;
}
public void setCssClass(String cssClass) {
this.cssClass = cssClass;
}
}
Where the cssClass is updated in the managed bean:
if(tone>0)
c.setCssClass("commentPos");
else if(tone<0)
c.setCssClass("commentNeg");
The class never gets assigned. Am I doing something wrong, or is this simply not possible?
In the standard JSF <h:dataTable> component, the rowClasses attribute is unfortunately not evaluated on a per-row basis. It's evaluated on a per-table basis. Component libraries like Tomahawk and PrimeFaces however support the kind of attribute which you're looking for on their <t:dataTable> and <p:dataTable>.
With the standard JSF <h:dataTable> component you need to supply a comma-separated string of all row classes. This can look something like this:
public String getRowClasses() {
StringBuilder rowClasses = new StringBuilder();
for (Comment comment : comments) {
if (rowClasses.length() > 0) rowClasses.append(",");
rowClasses.append(comment.getCssClass());
}
return rowClasses.toString();
}
which is then to be referenced as
<h:dataTable ... rowClasses="#{post.rowClasses}">
See also:
<h:dataTable> tag documentation - lists all attributes and the accepted values