Primefaces selectOneMenu not displaying initial value - jsf

I set up a selectOneMenu with POJOs and a converter, in a p:dialog, see the sources below. It does work, except that initially, when it is first displayed in not-dropped-down state, the first choice is selected, not the one corresponding to the bean value. If I save the state of the selectOneMenu without interacting with it at all, the initially selected first choice is saved and so the real value is overwritten, but if I select a differenct choice, it is saved properly. The bean value to which the selectOneMenu is bound can't be null.
I debugged the converter, and it turned out, that when the backing data is loaded and the dialog is refreshed and displayed, all of the choices go through the converter's getAsString(), plus the choice for the real bean value again. Still, the first choice gets actually selected and displayed in the selectOneMenu. When the dialog's form is commited, the actually selected choice goes through the converter's getAsObject(), regardless whether that was the wrongly selected initial value or the manually selected one.
Please advise what might be the problem.
The xhtml of the button that invokes the dialog, this is in a different form:
<p:commandButton id="toolbarEditButton"
value="Edit selected" update=":editMediaForm"
disabled="#{!contentManager.mediaSelected}"
actionListener="#{contentManager.editSelectedMedia}"
onclick="PF('editMediaWidget').show()" />
The xhtml of the dialog:
<p:dialog id="editMediaDialog" widgetVar="editMediaWidget"
modal="true" resizable="false" >
<h:form id="editMediaForm" >
<p:panelGrid rendered="#{contentManager.isMediaSelected()}" columns="2" >
... <!-- other properties of the selected element -->
<p:outputLabel value="Media type" />
<p:selectOneMenu value="#{contentManager.selectedMedia.mediaType}"
converter="#{mediaTypeConverter}">
<f:selectItems value="#{mediaTypeConverter.allMediaTypes}"
var="mt" itemLabel="#{mt.name}" itemValue="#{mt}" />
<p:ajax listener="#{contentManager.onMediaTypeChanged()}" />
</p:selectOneMenu>
</p:panelGrid>
</h:form>
</p:dialog>
The converter:
#Override
public Object getAsObject(FacesContext arg0, UIComponent arg1, String stringId) {
Long id = Long.valueOf(stringId);
for (MediaType mt : mediaTypes) {
if (mt.getPkid().equals(id)) {
return mt;
}
}
return null;
}
#Override
public String getAsString(FacesContext arg0, UIComponent arg1, Object mtObj) {
MediaType mt = (MediaType) mtObj;
return mt.getPkid().toString();
}
public List<MediaType> getAllMediaTypes() {
return mediaTypes;
}
Edit: the backing bean
#SessionScoped // javax.enterprise.context.SessionScoped
#Named("contentManager") // javax.inject.Named
public class ContentManager implements Serializable {
...
private List<Media> mediaList;
private Media selectedMedia = null;
...
public boolean isMediaSelected() {
if (selectedMedia == null) return false;
return true;
}
...
public void saveSelectedMedia() {
myDao.saveMedia(selectedMedia);
}
public void editSelectedMedia() {
// only for debugging, could be removed
}
}
The dialog is brought up and the form is updated by an edit button which is only available after an element is selected from a dataTable (selectedMedia). The update does seem to work, since the other properties of the selected element are properly updated and displayed in the dialog, so the bean value behind the selectOneMenu should be ok.
Update: of course I also examined the generated HTML. The <select> seems to be OK for me, it contains the proper values to be converted by the converter. (The selection is still wrong)
<select id="form:blah_input" name="form:blah_input" tabindex="-1">
<option value="1" selected="selected">1 - half_horizontal</option>
<option value="2">2 - half_vertical</option>
<!-- etc -->
</select>

The objects displayed in the SelectOneMenu have to have proper equals() method, not only the default Object#equals which is only true if they are the same object. This explains why the initial displayed value was always the first: the bean value never matched any of the possible values, so the SelectOneMenu component simply displayed the first one.
So the mistake was not in the JSF or backing bean code, but in the displayed domain objects' (MediaType) code. Adding the equals() method there solved the problem.

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.

Inside c:foreach set transient field of a JPA entity and retrieve it

I'm new to JSF. I have a c:foreach element that gets an images list from a bean. Each images list element is an Image object (created by me) that has a transient boolean field. This field initially is false. I also have an outputText to show the boolean value. Now I want that when a button is clicked the boolean field is set to true and the outputText is refreshed with the new boolean value. I tried but the boolean value is always false. Is this possible to do? It seems like the image where I set the boolean field is not the same where I get the boolean field, or maybe the image element in the foreach is not updated.
JSF code:
<c:forEach items="#{publicGalleryImageShowController.images}" var="img">
<h:form>
<h:commandButton
action="#{img.setShowComments(true)}"
value="Show/Hide">
<f:ajax render="co-#{img.id}" />
</h:commandButton>
</h:form>
<h:panelGroup id="co-#{img.id}">
<h:outputText value="#{img.showComments}" />
</h:panelGroup>
</c:forEach>
Java Image class with JPA annotation:
#Entity
public class Image {
private boolean showComments;
/*other fields*/
#Transient
public boolean isShowComments() {
return showComments;
}
public void setShowComments(boolean showComments) {
this.showComments = showComments;
}
/*other getters and setters*/
}
BalusC is right.
Your code is working
action="#{img.setShowComments(true)}"
I created a demo app on GitHub: https://github.com/simasch/46756639
Please check it out.

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.

how does mouse event work in primefaces?

i am developing a java web application using primefaces 4.0 and jsf 2.0.
i have a text label and it's textbox. when user is in edit mode, and want to modify the value of a particular textbox, the old value of the textbox should be displayed on the right side while the user is entering the new value in the textbox. so i add an output text which rendered false on load. i want to trigger this output text(id="test") when the user click in the textbox(id="customer_customername"). so rendered should be change to equal. anyone can tell me how to do this? in my backend i have an interface with its implementation , dao and service.
<h:panelGrid id="detail1" columns="2" styleClass="grid" columnClasses="label,value">
<h:outputText value="#{customermsgs['customer.customername.title']}:" />
<h:inputText id="customer_customername" value="# {CustomerComponent.customer.customername}" onclick="#{CustomerComponent.customername}" label="customer_customername">
<f:ajax render="detail1" />
</h:inputText>
<h:outputText id="test" value="#{CustomerComponent.customer.customername}" rendered="#{CustomerComponent.visible}"/>
</h:panelGrid>
public class CustomerComponentImpl implements CustomerComponent {
/**
* Data type variable that provides CRUD operations for Customer entities
*
*/
private boolean visible=false;
private String customername;
public String getCustomername() {
return customername;
}
public void setCustomername(String customername) {
setVisible(true);
this.customername = customername;
}
public boolean isVisible() {
return visible;
}
public void setVisible(boolean visible) {
this.visible = visible;
}
//some codes goes here.
Note: i have implemented the method in my interface also.
the onclick event is not working. look like it does not trigger! anyone can help?
This,
<h:inputText ... onclick="#{CustomerComponent.customername}">
does very definitely not what you thought it does. In its current form, when filled, it would only cause a JavaScript error because the #{CustomerComponent.customername} does very likely not return a piece of (syntactically valid!) JavaScript code which should be executed on click. Instead, it's more likely to return a string representing the customer name. When printed this into JavaScript context, it would only be interpreted as an undefined JavaScript variable.
As to your concrete functional requirement, the <f:ajax> listens inside input components by default only on change event. You can change this to click by setting the event attribute accordingly. All in all, this should do as you intented:
<h:inputText id="customer_customername" value="#{CustomerComponent.customer.customername}" label="customer_customername">
<f:ajax event="click" render="detail1" />
</h:inputText>

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