SelectOneMenu appears only the object and not the content that should - jsf

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.

Related

Custom Validator for p:selectOneMenu make it not working rendering / not rendering other components

I have this select:
<p:selectOneMenu value="#{bean.val}">
<f:selectItem itemValue="X" itemLabel="Select" />
<f:selectItems value="#{bean.getVals()}" />
<p:ajax update="wrapper" />
</p:selectOneMenu>
And this is the wrapper it updates:
<p:panel id="wrapper">
<p:panel rendered="#{bean.val == 'A' or bean.val == 'B'}">
<!-- insert your code here -->
</p:panel>
</p:panel>
The wrapper is outside the select, at the same level.
At the start, it's all ok. The wrapper is hidden.
If I select 'A' and then 'C', for example, the wrapper disappear. BUT, if I select 'A' or 'B' and 'X' again (the first "choice", Select), the wrapper does NOT disappear!
I have put a breakpoint inside the setter of bean.val. The setter in invoked for all the choices BUT for the first one the value inside the debugger is the same as the previous one!
BUT! If I remove the validator, it works!
This is the validator:
#FacesValidator(value="requiredSelect")
public class RequiredSelect implements Validator {
protected MessageUtil messageUtil = new MessageUtil();
#Override
public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
String val = (String) value;
if ("X".equals(val)) {
FacesMessage msg = this.messageUtil.getDefaultValidationError();
throw new ValidatorException(msg);
}
}
}
Previously I used another validator:
#FacesValidator(value="requiredString")
public class RequiredString implements Validator {
protected MessageUtil messageUtil = new MessageUtil();
#Override
public void validate(
FacesContext context,
UIComponent component,
Object value
) throws ValidatorException {
String val = (String) value;
if (val == null || val.trim().isEmpty()) {
FacesMessage msg = this.messageUtil.getDefaultValidationError();
throw new ValidatorException(msg);
}
}
}
but it did not work, because the empty string was not saved to the backing bean at menu change. So, if, for example, you selected 'A' and then back '' (Select) and you try to submit the form, the select is signaled with errors, but the value of the select returns to be 'A'! So the bogus 'X' value.
I'm using Primefaces 3.4.1 and Mojarra 2.1.7
Your issue is a bit of a XY-problem. You are trying to hack your way out of creating a required p:selectOneMenu using the X select item. This requires you to create a validator. If you simply make the p:selectOneMenu required, and would use null as the itemValue of the placeholder, it prevents the need to handle the X, so you don't need a validator.
Selecting null will cause validation to fail. So, once a selection is made you no longer want to render the null placeholder. Normally one would use the hideNoSelectionOption attribute of the p:selectOneMenu. But this is not available in PrimeFaces 3.4.1. As you don't want to upgrade, you can add this behavior by creating a custom renderer for the SelectOneMenu.
Create a new class, say my.custom.MySelectOneMenuRenderer, and extend SelectOneMenuRenderer. In this you want to #Override the encodeInput method to something like:
protected SelectItem selectMeOption = new SelectItem(null, "Select");
#Override
protected void encodeInput(
FacesContext context, SelectOneMenu menu, String clientId,
List<SelectItem> selectItems, Object values, Object submittedValues,
Converter converter
) throws IOException {
String classes = menu.getStyleClass();
Pattern pattern = Pattern.compile("\\bnormalSelect\\b");
Matcher matcher = pattern.matcher(classes);
boolean isNormal = matcher.find();
Object value = menu.getValue();
// If there's not a class "normalSelect" and no value is set, add the
// null option as the first item
if (!isNormal) {
if (value == null) {
selectItems.add(0, selectMeOption);
}
else {
SelectItem firstOption = selectItems.get(0);
if (selectMeOption.equals(firstOption)) {
selectItems.remove(0);
}
}
}
super.encodeInput(
context, menu, clientId, selectItems, values, submittedValues,
converter
);
}
Add your custom renderer to the render-kit section in your faces-config.xml like:
<render-kit>
<renderer>
<component-family>org.primefaces.component</component-family>
<renderer-type>org.primefaces.component.SelectOneMenuRenderer</renderer-type>
<renderer-class>my.custom.MySelectOneMenuRenderer</renderer-class>
</renderer>
</render-kit>
View example:
<p:selectOneMenu value="#{bean.val}" layout="pageDirection">
<f:selectItems value="#{bean.vals()}" />
<p:ajax update="#{empty bean.val ? '#this' : ''}" />
</p:selectOneMenu>
For a working example see: https://github.com/jepsar/primefaces-test/blob/so_68457571/src/main/webapp/test.xhtml
See also:
How to sort f:selectItems in each p:selectOneMenu of my application?
Please have a look at the JSF Life-Cycle for example here:
https://www.torsten-horn.de/img/JSF-Lifecycle.gif
And you will note, that if you throw "Validator-Exceptions" the "Update Model Values" Phase is skipped. (Process Events forwards to render response).
Hence your value "X", which you consider invalid in your validator is never send to the bean, and therefore the wrapper won't vanish.
What you actually want is:
Define a proper "NoSelect-Option"
Make the field "required".
Then - upon submission - the required field will be considered empty and throw a validator-exception where you need it.
ps.: A neat way to just bypass this "Issue" is to use hideNoSelectionOption on the selectOneMenu - that way, the form starts with "Please select", but the user can't switch back to "Please select", once he made an choice:
<p:selectOneMenu value="#{bean.val}" hideNoSelectionOption="#{not empty bean.val}">
<f:selectItem itemValue="#{null}" itemLabel="Select" noSelectionOption="true" />
<f:selectItems value="#{bean.getVals()}" />
<p:ajax update="#this,wrapper" process="#this" />
</p:selectOneMenu>
Updated, added #this to p:ajax.
PS: this does not work with old versions of Primefaces that does not have hideNoSelectionOption
Ok, this is my solution, thanks to the dognose's answer first part:
Change the input to:
<p:selectOneMenu value="#{bean.val}" required="true">
<f:selectItem itemValue="" itemLabel="Select" />
<f:selectItems value="#{bean.getVals()}" />
<p:ajax update="wrapper" />
</p:selectOneMenu>
No need to itemValue="#{none}", since null strings are automatically transformed to empty string by default, by jboss or primefaces, I do not remember. No need of noSelectionOption="true" too (I do not understand its scope... the validation works even if I don't put it)
added to a package my.package the files messages.properties, with this content:
javax.faces.component.UIInput.REQUIRED = Error
javax.faces.component.UIInput.REQUIRED_detail = One or more fields are missing or wrong. Please check the form
Then I added a p:messages like this one:
<p:messages id="messages" closable="true" showDetail="true" />
Then, I used this trick to remove the duplicate messages.

p:autoComplete shows entity Id after submit

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.

JSF SelectItems and escaping (xss)

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).

Jsf, Limitting on the number of caracteres to display?

I'm working on a jsf application. I have a dataTable, I use one column to render the description of a Pojo.
The problem is : This description it too long and the dataTable is not perfectly lisible.
Is there a way to limit the number of caracteres to render on the description column of this dataTable like : Description desc desc...
If you are using JSF 2, you can use the function of:abbreviate from the OmniFaces project.
Put the JAR file in your WEB-INF/lib, configure the namespace accordingly in your XHTML file like this:
<html ... xmlns:of="http://omnifaces.org/functions">
and then you can use it on any String property, like this:
<h:column>
<h:outputText value="#{of:abbreviate(someBean.myproperty, 20)}" />
</h:column>
See the demo here.
If you are getting the string from a database, try to use the Substring SQL function to truncate the data directly when you get it from the DB.
If not, you can try the solution below.
Let us say that the POJO class (call it MyPOJO) you are using contains a String attribute called description. Now the DataTable will be linked to a List or Map etc... of that object, lets make it an "ArrayList<MyPOJO> theList" for now.
What you can do is have another attribute in the same class MyPOJO. Declare that as String descriptionTruncated and in the getter method of that String write the following:
public String getDescriptionTruncated() {
if (this.description == null) {
return description;
} else if (this.description.length() <= 20 ) {
return description;
} else {
return description.substring(0,19);
}
}
We assume here that the desired length is 20.
And then in the Datatable reference the newly created String like this :
<h:datatable ... var="pojoObj">
<h:column>
<h:outputText value="#{pojoObj.descriptionTruncated}" />
</h:column>
Hope that helps.

JSF commandButton with immediate="true"

I have a situation where there is a selectOneMenu that has a value bound to a backing bean.
I need to have a button that doesn't update model values (that is why it has immediate="true" property).
That button's action method changes the value the selectOneMenu is bound to, but when the page is redisplayed the original value is displayed (the one that was submitted) and not the one set in the action method.
Any ideas why that is happening?
If I didn't explain the problem good enough please let me know.
EDIT:
As requested here is the source code in question:
page code:
<h:selectOneMenu id="selectedPerson"
binding="#{bindings.selectPersonComponent}"
value="#{bean.selectedPerson}">
<s:selectItems var="op" value="#{bean.allPersons}"
label="#{op.osoba.ime} #{op.osoba.prezime}"
noSelectionLabel="#{messages.selectAPerson}">
</s:selectItems>
<f:converter converterId="unmanagedEntityConverter" />
</h:selectOneMenu>
...
<a4j:commandButton action="#{bean.createNew}" value="#{messages.createNew}"
immediate="true" reRender="panelImovine">
</a4j:commandButton>
java code:
private Person selectedPerson;
public String createNew() {
log.debug("New created...");
selectedPerson = null;
bindings.getSelectPersonComponent().setSubmittedValue(null); //SOLUTION
return "";
}
The solution is in the lined marked SOLUTION :)
As it frequently happens a few moments after posting this question I have found an answer:
The cause of the problem is in detail explained here: ClearInputComponents
The problem is (as explained) that model values haven't been updated so the submitted inputs are still in component.submittedValue field and that field is displayed if not empty. It is emptied normally after model has been updated.
The first solution didn't work in my case because there is other important state in the view that mustn't get lost. But the second solution worked great:
component.setSubmittedValue(null);
And that was all that was needed: it is a little extra work because components must be bound to some bean, but not that bad.
To follow up a bit, I don't think you need to bind the component to a bean. You can just grab the component from the FacesContext instance via the UIViewRoot, if you know the component's client ID.
It would go a little something like this:
Foo component = (Foo)FacesContext.getCurrentInstance().getViewRoot().getComponent(clientId);
Where Foo is the class of the component you are using and clientId is the client ID of the component in the "formId:elementId" format that JSF uses.
For me, this has worked:
#ManagedBean(name = "bean")
#ViewScoped
public class Bean {
private SelectOneMenu component;
public SelectOneMenu getComponent() {
return selectComponent;
}
public void setComponent(SelectOneMenu component) {
this.Component = component;
}
public void resetComponent() {
component.resetValue();
}
...
}
<h:selectOneRadio value="#{bean.value}" id = "idRadio" required="true" requiredMessage = "Required Message" binding="#{bean.component}" >
<f:selectItem itemLabel="Value 1" itemValue="value1"/>
<f:selectItem itemLabel="Value 2" itemValue="value2" />
</h:selectOneRadio>
<primefaces:commandButton action="#{bean.resetComponent}" value="Erase" update="idRadio" immediate="true"/>
...
Thanks.

Resources