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).
Related
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 would like to create a p:selectOneMenu item where the values and selected value display translated strings. So if the user has switched their language to french, the dropdown contents and selection will all be displayed in french, etc.
Currently I have a list of values to display, each of which knows what their string value is in each language (I realize this is probably an antipattern, will refactor once this is working.)
I've solved the issue of displaying the drop down elements in translated fashion using the following code. I've also created a subclass of the Omnifaces SelectItemsConverter class to get a translated string of each object.
<p:selectOneMenu
value="#{linkagecontroller.selectedLink}"
converter="linkageTypeSelectItemsConverter"
var="lnk">
<f:selectItems value="#{linkagecontroller.linkageTypes}"
var="item"/>
<p:column>
<div class="link-item-#{lnk.linkageTypeId}">
<h:outputText value="#{lnk.getKeyValue(language.localeCode)}" />
</div>
</p:column>
</p:selectOneMenu>
LinkageTypeSelectItemsConverter
#FacesConverter("linkageTypeSelectItemsConverter")
public class LinkageTypeSelectItemsConverter extends SelectItemsConverter implements Serializable {
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
final String locale = context.getApplication().evaluateExpressionGet(context,
"#{language.localeCode}",
String.class);
if (value instanceof LinkageTypeKey) {
return ((LinkageTypeKey) value).getKeyValue(locale);
} else {
return super.getAsString(context, component, value);
}
}
}
When the form is displayed, I can debug this converter, it's returning values translated, but the selected element ID is always being displayed in the default language, english. Thoughts?
The converter is for converting item values, not item labels.
You need to explicitly specify itemLabel.
<p:selectOneMenu
value="#{linkagecontroller.selectedLink}"
converter="omnifaces.SelectItemsConverter">
<f:selectItems value="#{linkagecontroller.linkageTypes}" var="item"
itemValue="#{item}" itemLabel="#{item.getKeyValue(language.localeCode)}" />
</p:selectOneMenu>
This is only a somewhat strange approach of localization. You usually put translations in a resource bundle instead of in the model itself. It could even be done without a converter if it's an enum.
See also:
How to use enum values in f:selectItem(s)
Localizing enum values in resource bundle.
I have different objects which all implements the same interfaces. All of these objects need to be selectable by <p:selectCheckboxMenu/>. Default values for those objects and selected values are placed in same Map<?,?>, and few such Maps grouped together inside another Map. It sounds complicated but please look at the code below and all will be clear.
When I select an object, converter get a list of all objects from MyBean (CDI bean), looking by uuid required object and return it, without throwing any exception. The problem begins when I try to work with selected objects. For example this line of code inside:
onObjectChange() method from MyBean:
List<AllObjects> objects= objectContainer.getControllers().get("Object 1").get("selected");
throws an exception:
java.lang.ClassCastException: [Ljava.lang.Object; incompatible with java.util.List
And indeed when I hover mouse over objectContainer I see that it contains object of type selected=[Ljava.lang.Object;#dba1b6e7} But when I evaluate same line of code inside Expressions panel of Eclipse I get the required values: MyObject1#d8f0f5f8
I don't understand in general is it possible to do what am I doing, i.e. few different objects with same interface to be selectable by <p:selectCheckboxMenu/>. And if yes, why do I have this casting problem? My colleague said that it might be a problem with my converter and I tend to agree with she, but don't know is it correct and if yes, how to solve it.
UPDATE: It looks that problem not inside Converter but due to fact that I pass Collection to collect selected values dynamically, as a value of <ui:param/>. I pass it as List<AllObjects> and get it back as Object. I can cast it then to Object[] and every object inside it to appropriated object by using introspection and it works. But why it mutates initial object? It shouldn't do this.
Thank you in advance and my code below:
This is an interface:
public interfaces AllObjects{
public String getName();
}
There are multiple objects, MyObject, MyObject1, MyObject2 which implement interfaces above:
public MyObject implements AllObjects{
...
}
This is my bean and how my objects are initialized:
public MyBean {
Map<String, Map<String,List<AllObjects>>> objectContainer = new LinkedHashMap<String, Map<String,List<AllObjects>>>();
public void init(){
Map<String,List<AllObjects>> nameValuesPairs1 = new LinkedHashMap<String,List<AllObjects>>();
List<AllObjects> allSelectedObjects1 = new ArrayList<AllObjects>();
List<AllObjects> allDefaultObjects1 = new ArrayList<AllObjects>();
nameValuesPairs.put("default",allDefaultObjects1);
nameValuesPairs.put("selected",allSelectedObjects1);
Map<String,List<AllObjects>> nameValuesPairs2 = new LinkedHashMap<String,List<AllObjects>>();
List<AllObjects> allSelectedObjects2 = new ArrayList<AllObjects>();
List<AllObjects> allDefaultObjects2 = new ArrayList<AllObjects>();
nameValuesPairs.put("default",allDefaultObjects2);
nameValuesPairs.put("selected",allSelectedObjects2);
objectContainer.put("Object 1", nameValuesPairs1);
objectContainer.put("Object 2", nameValuesPairs2);
}
public void onObjectChange(){
...
List<AllObjects> objects= objectContainer .getControllers().get("Object 1").get("selected"); //throws exception
...
}
}
My *.xhtml page:
<h:panelGroup id="object_panel">
<ui:repeat id="objects_id" var="object" value="#{myBean.objectContainer.entrySet().toArray()}">
<p:selectCheckboxMenu
value="#{object.value['selected']}" label="#{object.key}"
converter="#{myObjectConverter}"
filter="true"
filterMatchMode="startsWith"
panelStyle="width:250px">
<f:selectItems value="#{object.value['default']}" var="value" itemValue="#{value}" itemLabel="#{value.name}" />
<p:ajax event="change" process="#this #parent" listener="#{myBean.onObjectChange}"/>
</p:selectCheckboxMenu>
</ui:repeat>
</h:panelGroup>
And converter:
public class ChartParameterConverter implements Converter, Serializable {
#Inject
private MyBean myBean;
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) throws ConverterException {
AllObjects result = null;
...
//here to result assigned MyObject1 or MyObject2 type depends on condition and it being returned
...
return result;
}
...
}
Ok, it looks that <ui:repeat> and <p:selectCheckboxMenu> not work well with DataModel which I used, Map<String,Map<String,List<MyObject>>>. I've changed my DataModel in the following way:
public ObjectContainer{
private String name;
private List<MyObject> defaultObjects;
private List<MyObject> selectedObbjects;
}
and pass to <ui:param> it as List<ObjectContainer>. so my *.xhtml page looks in the following way:
<p:selectCheckboxMenu
value="#{object.selectedObjects}" label="#{object.name}"
converter="#{myObjectConverter}"
filter="true"
filterMatchMode="startsWith"
panelStyle="width:250px">
<f:selectItems value="#{object.defaultObjects}" var="value" itemValue="#{value}" itemLabel="#{value.name}" />
<p:ajax event="change" process="#this #parent" listener="#{myBean.onObjectChange}"/>
</p:selectCheckboxMenu>
</ui:repeat>
And now everything works as it should.
And I throwed away my custom converter and use SelectItemsConverter from Omnifaces's library. Highly recommended change, code become much simpler and readable.
You could do a unique Tuple for each Object, The Tuple must be converted to unique String.. And you could have the posible values in a Array..
static Map<String,Object> uniques = new LinkedHashMap<>();
static{
//you could save the possible values in a Singleton Bean
uniques.put(key,value)...
}
In your Converter
getAsString -> Return a Key from Value
getAsObject -> Return a Value by Key
This question may be more of the type "conceptual" or "I don't understand JSF".
My scenario:
I have a JSF Page (index.xhtml) where I use a p:accordionPanel (but I don't think it matters what component it is). What I want to do is to set the activeIndexes of it.
<p:accordionPanel multiple="true" activeIndex="#{myController.getActiveIndexesForSections('whatever')}">
// bla bla...
</p:accordionPanel>
And the (simplified) method in the backing bean:
public String getActiveIndexesForSections(String holderName){
String activeSections = "";
for(Section s : sectionMap.get(holderName)){
if (s.isActive())
//add to the string
}
return activeSections;
}
Now this works just fine on a normal page load.
But if I click on a p:commandButton (with ajax=false) (or anything else which "sends" data back to the server I guess) - I get the following exception:
/WEB-INF/tags/normalTextSection.xhtml #8,112 activeIndex="#{myController.getActiveIndexesForSections(name)}": Illegal Syntax for Set Operation
// bla..
Caused by: javax.el.PropertyNotWritableException: Illegal Syntax for Set Operation
After some googling / reading the error message I found that I need a setter.
First of all: I don't want a setter - do I really need one or is there a way to tell JSF I don't want this "behavior".
Second I realized that it's not that "easy" to provide a setter, because my method has a parameter (so public void setActiveIndexesForSections(String name, String activeIndexes) or public void setActiveIndexesForSections(String name)won't work).
What I came up with in the end is:
Create a (generic) "Pseudo-Property-class":
// just a dummy class since the class is recreated at every request
public class Property<T> implements Serializable {
private T val;
public Property(T val) {
this.val= val;
}
public T getVal() {
return val;
}
//no need to do anyhting
public void setVal(T val) {
}
}
Change the bean method:
public Property<String> getActiveIndexesForSections(String holderName){
String activeSections = "";
for(Section s : sectionMap.get(holderName)){
if (s.isActive())
//add to the string
}
return new Property<String>(activeSections);
}
And call it from the index.xhtml:
<p:accordionPanel multiple="true" activeIndex="#{myController.getActiveIndexesForSections('whatever').val}">
// bla bla...
</p:accordionPanel>
This works but obviously is a ugly hack/workaround.
What is the proper way to handle a situation like this? Or is what I'm doing simply completely wrong?
The setter is needed to remember the active indexes as they were when the form is submitted. Basically, you need to bind it as a value expression (with a property), not as a method expression (like an action method), nor to an unmodifiable collection (like activeIndex="#{param.tab}"). Exactly like as with input values. Technically, you're indeed doing it "simply completely wrong" ;)
The requirement is however understood. Given that you're really not interested in the changed active indexes, and thus want to reset them to defaults on every form submit, then you can bypass it by storing the result as a request attribute with help of <c:set>. This way you will fool EL to set it in the request attribute map instead of the intented bean property.
<c:set var="activeIndex" value="#{myController.getActiveIndexesForSections('whatever')}" scope="request" />
<p:accordionPanel multiple="true" activeIndex="#{activeIndex}">
<!-- bla bla... -->
</p:accordionPanel>
Under the covers, it will basically do externalContext.getRequestMap().put("activeIndex", value) as setter operation, which will obviously just work.
Update: upon inspecting the source code of AccordionPanel component, I saw another workaround given the fact that the activeIndex won't be set when the rendered attribute evaluates false. So just alter the rendered attribute to behave exactly that: evaluate false during update model values phase (the 4th phase).
<p:accordionPanel multiple="true"
activeIndex="#{myController.getActiveIndexesForSections('whatever')}"
rendered="#{facesContext.currentPhaseId.ordinal ne 4}">
<!-- bla bla... -->
</p:accordionPanel>
I have a selectOneMenu that loads a list of BD, debugging can ve that all steps are correct except the time it shows the value in selectOneMenu. On page appears the object name "JanelaPortabilidade # 7437" and not like the String.
My codes:
public List<SelectItem> listarHoraJanela(DateSelectEvent event) {
dataF = event.getDate();
Calendar dataAux = new GregorianCalendar();
dataAux.setTime(dataF);
//Corrigir
if (listaHoraJanela == null) {
listaHoraJanela = new ArrayList<JanelaEAPortabilidade>();
listaHoraJanela = consultaJPortabilidade.listarHoraJanela(dataAux);
listSelectHoraJ.add(new SelectItem("", ""));
for (JanelaEAPortabilidade horaJ : listaHoraJanela) {
String dataFormat = String.format("%02d",horaJ.horIniJanela.get(Calendar.HOUR_OF_DAY));
System.out.println(dataFormat);//So here is ok, print in console correct string
listSelectHoraJ.add(new SelectItem(horaJ.getIdtJanela(),dataFormat));
}
}
xhtml:
<h:outputText value="Hora da Janela:" for="horaJanela" />
<h:selectOneMenu value="#{bean.codHoraJanela}" id="horaJanela"
rendered="#{bean.type == 'Fixo'}" >
<f:selectItems value="#{bean.listaHoraJanela}" />
What can it be? I must be missing something.
Thank you!
What you need to do is to use the itemLabel and itemValue attributes for f:selectItems. Here is an example from the PrimeFaces website (http://www.primefaces.org/showcase-labs/ui/selectOneMenu.jsf from the pojo section of the code):
<f:selectItems value="#{autoCompleteBean.players}" var="player" itemLabel="#{player.name}" itemValue="#{player}"/>
The itemLabel is what will be displayed in the drop down list and itemValue is what will be returned (in #{bean.codHoraJanela} in your example).
I'm surprised the code even works. If I didn't miss something the code should throw a NullPointerException. In the JSF you posted a DateSelectEvent isn't created, instead the method is called without any arguments. The DateSelectEvent in the Java method should therefor be NULL.
The output you see is the result of an object's toString() method. Probably the object represented there is coming from the codHoraJanela method in the managed bean. Fix the listarHoraJanela method to return a list of objects.