Event Function called before Setter - jsf

I have the following dropdown which list couple of cars, I have it so that it stores the value of selected item in a backbean variable and an event is fired so other dropdowns would fill up according to the selected value of this dropdown control as below:
<Td>
<h:selectOneMenu id="combocarList"
value="#{customerBean.selectedcar}"
styleClass="comboStyle"
valueChangeListener="#{customerBean.loadothercombos}"
onchange="document.forms[0].submit()"
>
<f:selectItem
itemLabel="-----------Select--------------"
itemValue="None" />
<f:selectItems value="#{customerBean.carsList}" />
</h:selectOneMenu>
</Td>
Problem is when an item selected from the above dropdown, the event loadothercombos is called before the setter which causes problems.
Note that the backbean customer is defined as:
<managed-bean-name>customerBean</managed-bean-name>
<managed-bean-class>com.theway.customer</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
The behaviour i see in debugging that when i select an item from dropdown:
1) Getter is called for selectedcar
2) Loadothercombos is called <------- This is called by the event
3) Setter is called for selectedcar
I cannot get it to call the setter before calling loadothercombos. Any insight would be appreciated. Thanks

Using valueChangeListener for this purpose has always been a hacky way. Basically, there are two ways to work around this "problem":
Call FacesContext#renderResponse() so that JSF will immediately move to the render response phase and thus the update model values (and invoke action) phase will be skipped:
public void loadothercombos(ValueChangeEvent event) {
selectedcar = (String) event.getNewValue();
loadOtherCombosBasedOn(selectedcar);
// ...
FacesContext.getCurrentInstance().renderResponse();
}
Queue the event to invoke action phase so that it does its job after the setter being called:
public void loadothercombos(ValueChangeEvent event) {
if (event.getPhaseId() == PhaseId.INVOKE_APPLICATION) {
loadOtherCombosBasedOn(selectedcar);
} else {
event.setPhaseId(PhaseId.INVOKE_APPLICATION);
event.queue();
}
}
If you're using JSF 2.0, then there's a much easier way to handle this with help of <f:ajax>:
<h:selectOneMenu id="combocarList"
value="#{customerBean.selectedcar}"
styleClass="comboStyle">
<f:selectItem
itemLabel="-----------Select--------------"
itemValue="None" />
<f:selectItems value="#{customerBean.carsList}" />
<f:ajax listener="#{customerBean.loadOtherCombos}" render="otherComboIds" />
</h:selectOneMenu>
with
public void loadothercombos() {
loadOtherCombosBasedOn(selectedcar);
}
Unrelated to the concrete problem: a "combobox" is the incorrect term for this dropdown element. A combobox is an editable dropdown, it's basically a combination of <input type="text"> and <select>. What you've there just renders alone <select> and those are just dropdowns, so call them as such as well.

Problem is when an item selected from the above dropdown, the event
loadothercombos is called before the setter
Well, is the expected JSF life cycle behavior. Your valueChangeListener="#{customerBean.loadothercombos}" is invoked during validation phase after a succesful conversion/validation of the submitted value and only when the submitted value differs from the initial value. After invoking the valueChangeListener, JSF will continue with conversion/validation of the next UIInput and when JSF implementation determines that the data is valid, then proceed to the next Update Model Values Phase invoking your setter method value="#{customerBean.selectedcar}"

Related

Primefaces selectOneMenu not displaying initial value

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.

Update Action in Bean for JSF

Although it is a simple question . Need an answer for this.
I have written a jsp page as :
<body>
<f:view>
<f:loadBundle basename="message" var="msg"/>
<h:form id="edit">
<h:panelGroup>
<h:selectOneRadio id="CompletePublication" layout="pageDirection">
<f:selectItem itemLabel="SUCCESS" itemValue="true"/>
<f:selectItem itemLabel="FAILED" itemValue="false"/>
</h:selectOneRadio>
</h:panelGroup>
<h:commandButton id="setAction" immediate="true" action="#{user.completePublication}" value="#{msg.Button_OK}"/>
<h:commandButton id="cancel" immediate="true" action="#{user.cancelCompletePublish}" value="#{msg.Button_CANCEL}"/>
</h:form>
</f:view>
</body>
It needs to be handled under theBean:
public class User implements Serializable {
private String name;
private boolean action;
public boolean getAction()
{
System.out.println("Get");
return action;
}
public void setAction(boolean action)
{
this.action=action;
System.out.println("Set");
}
public String completePublication(){
if (action==true){
System.out.println("Value of True Action - " + action);
return "updated";
}
if (action==false){
System.out.println("Value of False Action - " + action);
return "notupdated";
}
return null;
}
public String cancelCompletePublish()
{
System.out.println("Hi");
return null;
}
}
So can any one help on this. At the output evertime i see the "Value of False Action - False"
In your <h:selectOneRadio> you are not passing the value of the selected choice back to your bean when the form is submitted. Therefore, the action variable will never get updated. Assuming that you refer to your managed bean as user in the page, you will need to modify that component by adding the value attribute. Therefore, change
<h:selectOneRadio id="CompletePublication" layout="pageDirection">
to
<h:selectOneRadio id="CompletePublication" value="#{user.action}" layout="pageDirection">
For more information on <h:selectOneRadio> please refer to this link. Also, as mentioned by #Xtreme Biker, you need to understand what immediate="true" does when it is applied to a particular component. For your case, when a user hits any of the <h:commandButton>, the Update Model Phase (as well as other phases) will be skipped. In other words, action will never be set to true when the user selects SUCCESS and then clicks the OK (first) button. That's why you always see Value of False Action - False in your output. To fix this just remove the immediate attribute in the first <h:commandButton> like so
<h:commandButton id="setAction" action="#{user.completePublication}" value="#{msg.Button_OK}"/>
After removing that attribute, make the following change in your completePublication method
public String completePublication(){
if (action){
System.out.println("Value of True Action - " + action);
return "updated";
}
else {
System.out.println("Value of False Action - " + action);
return "notupdated";
}
}
There is no need to return null since action will either be true or false.
NOTE I intentionally did not go into detail on the behavior of the immediate attribute. If you want to understand it, you will need to spend some time to try and understand the JSF lifecycle phases. In fact, you will need to be somewhat comfortable with these concepts as you dive deeper into JSF. Both #johny and #Xtreme Biker gave you some good links to start with. I am copying them below just in case their comments get erased.
Debug JSF Lifecycle
Doubt on immediate attribute for command button

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>

disabled="#{not recordsTransferSearch.isDisabled}" does not call isDisabled() method

I'm trying to disable a jsf component just by defining a action method and bind it to the components "disabled" attribute.
My JSF component snippet
<h:form id="bulk_sch_form1">
<a4j:commandButton id="alls" value="a. Search records form this company"
action="#{recordsTransferSearch.actionSearch}"
reRender="srtDlGrd, dlod_msg_grd, pending_student_table"
disabled="#{not recordsTransferSearch.isDisabled}">
</a4j:commandButton>
</h:form>
Backing bean action method
public boolean isDisabled() {
if (searchResults != null) {
return true;
}
return false;
}
The searchResults just evaluating after a successful search result has been returned. But as stated in the title it's not calling the action method isDisabled() at all, thus nothing happens.
The action method is only calling when I'm refreshing the page.
Thanks.
You should either use disabled as a field getter, like in disabled="#{not recordsTransferSearch.disabled}", or if your EL supports method calls, i.e. is of version 2.2+, you should add empty parentheses, (), at the end of the method call, like in disabled="#{not recordsTransferSearch.isDisabled()}".
Note that as it stands, and contrary to what you suggest in comments, disabled="#{bean.isDisabled}" will trigger Property 'isDisabled' not found error.
As per your comments you do not fully understand how disabled attribute works in JSF. It seems that you expect the button to become enabled/disabled on some javascript event and/or via some changes made by some action/actionlistener methods. This is not the case. The button is disabled/enabled only when EL expression of disabled attribute evaluates to true/false correspondingly. You may even test it: when you remove the disabled attribute of HTML input, effectively making it enabled in client side and will call that button, you'll see that no action method will be called, but instead its disabled attribute will be reevaluated on server and, as it'll evaluate to false, no method will be called.
To make it work as expected, you need to rerender the command button via AJAX call (by specifying its id in reRender attribute of another <a4j:commandButton> that changes the result of isDisabled() method so that it'll return false), or synchronously (enforcing the needed evaluation of disabled) so that the disabled condition will be evaluated to false.
Also, it would be good to go through a basic example to get a grasp of how it all works.
The view:
<h:form>
<h:commandButton id="disabled" value="Disabled command button"
action="#{bean.disabledSubmit}"
disabled="#{not bean.disabled}">
</h:commandButton>
<h:commandButton id="simple" value="Enable a disabled button"
action="#{bean.simpleSubmit}">
<f:ajax render="disabled"/>
</h:commandButton>
</h:form>
The bean:
#ManagedBean
#ViewScoped
public class Bean implements Serializable{
private boolean searchResults = false;
public boolean isDisabled() {
return searchResults;
}
public String disabledSubmit() {
return null;
}
public String simpleSubmit() {
searchResults = true;
return null;
}
}

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