This is an update on a previous post I made regarding conditional rendering of page components. I can now conditionally render different components on the page based on selected user inputs using the f:ajax tag. The trouble is that when I click my commandbutton the inputs that are conditionally rendered using ajax aren't being read on submit. I am guessing it is because the whole page isn't rerendered when I add in the new components and the submit button can't see them, even though they are there. Here is the segment of my form page(it is wrapped in a h:form tag):
<h:outputLabel for="selectPersonType" value="Select Type: "/>
<h:selectOneMenu id="selectPersonType" value="#{addPersonBean.personType}" label="Person Type" required="true">
<f:selectItem itemValue=""/>
<f:selectItems value="#{listBean.personTypes}" />
<f:ajax render="results"/>
</h:selectOneMenu>
<h:message for="selectPersonType" style="color: red"/>
</h:panelGrid>
<h:panelGroup id="results">
<h:panelGroup rendered="#{addPersonBean.personType == 'STUDENT'}">
<h:outputLabel for="selectSchoolType" value="School Type: " />
<h:selectOneMenu id="selectSchoolType" value="#{addPersonBean.schoolType}">
<f:selectItems value="#{listBean.schoolTypes}" />
<f:ajax execute="selectSchoolType"/>
</h:selectOneMenu>
</h:panelGroup>
<h:panelGroup rendered="#{addPersonBean.personType == 'PATIENT'}">
<h:outputLabel for="smoker" value="Smoker? " />
<h:selectBooleanCheckbox id="smoker" value="#{addPersonBean.smoker}">
<f:ajax execute="smoker"/>
</h:selectBooleanCheckbox>
</h:panelGroup>
</h:panelGroup>
<h:commandButton value="Add Person" action="#{addPersonBean.action}"/>
The trouble here is that the components with ids 'selectSchoolType' and 'smoker's values don't get set when I click the commandButton because they are rendered conditionally using the selectPersonType select menu after the page has loaded. Is there a way to fire the components when their values change instead of when I click the submit button (therefore, their values should be processed before I even click submit). As you can see I have tried to use f:ajax with the execute attribute attached to the components in question but it didn't seem to work. (I believe the default events for these components are valueChange so haven't added them to the f:ajax tags, but I have tried 'change' and 'valueChange').
All your <f:ajax> things are inside a conditionally rendered component. This construct will fail when the condition behind rendered attribute evaluates to false during processing the form submit. This will happen when the #{addPersonBean} is in the request scope and doesn't preinitialize the condition behind the rendered attribute during (post)construction, or when it's performing business logic in getters/setters in a wrong way.
Placing the #{addPersonBean} in the view scope and ensuring that you aren't doing business logic in getters/setters should fix this problem.
See also:
commandButton/commandLink/ajax action/listener method not invoked or input value not updated - point 5 applies to you
How to choose the right bean scope?
Related
In my application I have implemented a page where the user can edit its account information. Since there are a lot of fields I have broken them up into p:tab elements inside a p:tabView. Like this:
<p:tabView dynamic="true" rendered="#{account.accessOK}">
<p:tab title="Basic Information">
<h:form id="formBasic">
(some fields here)
<p:commandButton value="Save"
action="#{account.doSave()}"
update="formBasic msgs"
/>
</h:form>
</p:tab>
<p:tab title="Address">
<h:form id="formAddress">
(fields related to address here)
<p:commandButton value="Save"
action="#{account.doSave()}"
update="formBasic msgs"
/>
</h:form>
</p:tab>
<p:tab title="E-Mail & Phone Numbers">
(then another form here with more field)
</p:tab>
<p:tab title="Password">
<h:form id="formPass">
<p:panelGrid columns="3">
(and this form handles the password)
<p:commandButton value="Change"
action="#{account.doPassword()}"
update="msgs"
/>
</h:form>
</p:tab>
</p:tabView>
This seems pretty straightforward but there is a problem. Each tab has its own form so that the fields that gets processed when the user hits the p:commandButton are only those in the form. However the following sequence will probably confuse the user:
Change some fields on one tab but do not click the Save button.
Go to another tab and fill in fields there, then click the Save button. The fields on that form will be processed.
Go back to the tab edited in step 1 above and the fields will look like they have been processed, but have not. It will be a filled out form but no visual way to tell that the fields have not been saved.
Note that I do not want to put all the tabs into one form and execute all the fields on all the tabs at once. This is because some field changes might have side effects, such as setting the password. Also you don't want a validation pass to happen on fields that the user isn't changing during this visit.
So is there any method to solve this user interface deficiency?
I have an application up and running. I have a page addstudent.xhtml page where the user adds the student details.When user finishes adding and submits he will be redirected displaystudent.xhtml page.
My question is when user directly types on browser
localHost:8081/Student/displaystudent.xhtml. Page is displayed with blank values.This should not happen. How do I prevent this even though he types on browser.
Both the files addstudent.xhtml displaystudent.xhtml pages are in web-inf folder. I am using Jsf2, primefaces 3.5
Make displaystudent.xhtml an include file which you render conditionally in addstudent.xhtml depending on whether the student is successfully added.
E.g., assuming standard JSF:
<h:panelGroup id="addStudent" layout="block">
<h:form rendered="#{empty bean.student.id}">
<h2>Add student</h2>
<h:inputText value="#{bean.student.name}" />
...
<h:commandButton value="Add" action="#{bean.add}">
<f:ajax execute="#form" render=":addStudent" />
</h:commandButton>
</h:form>
<h:panelGroup layout="block" rendered="#{not empty bean.student.id}">
<h2>Added student</h2>
<dl>
<dt>Student name</dt><dd>#{bean.student.name}</dd>
</dl>
</h:panelGroup>
</h:panelGroup>
(simplified; you could of course split off the add student to <ui:include src="/WEB-INF/displaystudent.xhtml"> and so on)
The button, whereby the add() method should return void or null, re-renders the <h:panelGroup id="addStudent">. If the student was successfully been added in the DB, then it should have a non-empty ID. This will cause the form to be hidden and the results panel to be shown.
See also:
Conditionally displaying JSF components
How to navigate in JSF? How to make URL reflect current page (and not previous one)
I want to render only one of two different component which will have same value in the bean based a check box. I was able to do so using the rendered attribute but the values are not re-rendered, it gets reflected only when i invoke action. What am i missing here?
<h:selectBooleanCheckbox value="#{bean.mask}">
<f:ajax event="click" listener="#{Bean.mask}" execute="#form" render="#form"/>
</h:selectBooleanCheckbox>
<h:inputSecret id="id1" value="#{Bean.value}" rendered="#{Bean.mask}" redisplay="true"/>
<h:inputText id="id2" value="#{Bean.value}" rendered="#{!Bean.mask}"/>
PS: I am able to see the value in the bean. Only thing is the value is not updated in the text box or secret box.
I have a form that is defined in a separate jsf page. This is is included to the main page when i click a link. Now the form is being displayed correctly. But the problem is that the submit button is not calling the action function defined.
The code to include the page( As suggested in this question: JSF2 Dynamically loading pages by ajax
<h:panelGroup id="editdivparent" layout="block">
<h:panelGroup id="editdiv" rendered="#{formsBean.edituserdiv}" layout="block">
<h:form id="userform" class="form-horizontal">
<ui:include src="edituserdetails.xhtml">
</ui:include>
</h:form>
</h:panelGroup>
</h:panelGroup>
The included page contains just the form elements with submit button:
<h:commandButton action="#{userBean.register() }" value="Update">
</h:commandButton>
I am getting no errors. On submitting the form the current page is redisplayed. I have put some print statements in the action function. Also there is a query error is put. None of them are being generated.
Am i doing something wrong here?
The commandButton action property already expects a method expression, so just take the parenthesis out, like this:
<h:commandButton action="#{userBean.register}" value="Update" />
Also, make sure you don't have nested forms when making templates with includes.
I hope it helps.
Using PF 3.0-RC1 Snapshot (11/22/2011)
I have a in a composite component.
I want to call the valueChangeListener when a selection is made, but it does not appear to be calling the listener.
Here is the code for the component:
<p:selectOneMenu style="width: 220px;"
value="#{customerProfileSessionBean.selectedAccount}"
valueChangeListener="#{customerProfileSessionBean.accountValueChange}" >
<f:selectItems value="#{sessionBean1.custAccountList}"/>
</p:selectOneMenu>
The listener in the backing bean has a print statement that is not being called (at least I do not see it in the server log).
Is there something else that I need to do to get the valueChangeListener to be called when the value changes?
Do I need to use ?
Also, in the listener, is there a ValueChangeEvent that is passed?
Thanks.
You seem to be expecting that the valueChangeListener method in the server side is called immediately when a change event occurs on the client side. This is not correct. It will only be invoked when the form is submitted to the server and the new value does not equals() the old value.
There are at least two ways to achieve your functional requirement:
Add onchange="submit()" so that JavaScript will submit the form whenever you change the value:
<p:selectOneMenu style="width: 220px;"
value="#{customerProfileSessionBean.selectedAccount}"
valueChangeListener="#{customerProfileSessionBean.accountValueChange}"
onchange="submit()">
<f:selectItems value="#{sessionBean1.custAccountList}"/>
</p:selectOneMenu>
This is however very JSF-1.x-ish and poor for user experience. It will also submit (and convert/validate!) all other input fields which may not be what you want.
Make use of an ajax listener instead, for sure if you are not interested in the actual value change (i.e. the old value is not interesting for you), but you're actually interested in the change event itself. You can do this using <f:ajax> or in PrimeFaces components using <p:ajax>:
<p:selectOneMenu style="width: 220px;"
value="#{customerProfileSessionBean.selectedAccount}">
<p:ajax listener="#{customerProfileSessionBean.accountValueChange}" />
<f:selectItems value="#{sessionBean1.custAccountList}"/>
</p:selectOneMenu>
And replace the ValueChangeEvent argument by the AjaxBehaviorEvent argument.
Add f:ajax statement for p:selectOneMenu , it will create ajax call and your value will be submitted EX:
<p:selectOneMenu style="width: 220px;" value="#
{customerProfileSessionBean.selectedAccount}"
valueChangeListener="#{customerProfileSessionBean.accountValueChange}" >
<f:selectItems value="#{sessionBean1.custAccountList}"/>
<f:ajax render="#form"/>
</p:selectOneMenu>
Or you can provide panel id or datatable id ,if you dont want torender the whole form, like:
<f:ajax render=":formid:panelGroupId"/>