h:inputText is not ajax-rendered on change of h:selectOneMenu - ajax

I have this code which is used to select how many rows to display in jsf table:
<!-- Set rows per page -->
<h:outputLabel for="rowsPerPage" value="Rows per page" />
<h:inputText id="rowsPerPage" value="#{AccountsController.rowsPerPage}" size="3" maxlength="3" />
<h:commandButton styleClass="bimage" value="Set" action="#{AccountsController.pageFirst}" >
<f:ajax render="#form" execute="#form"></f:ajax>
</h:commandButton>
<h:message for="rowsPerPage" errorStyle="color: red;" />
I want to edit the code this way: I want to replace the code with h:selectOneManu and option to insert custom value with AJAX support:
<h:selectOneMenu id="setrows" value="#{AccountsController.rowsPerPage}" converter="javax.faces.Integer" maxlength="3">
<f:selectItem itemValue="10" itemLabel="10" />
<f:selectItem itemValue="50" itemLabel="50" />
<f:selectItem itemValue="100" itemLabel="100" />
<f:selectItem itemValue="500" itemLabel="500" />
<f:selectItem itemValue="332" itemLabel="Custom" />
<f:ajax render="customrowperpage" />
</h:selectOneMenu>
<h:inputText id="customrowperpage" value="#{AccountsController.rowsPerPage}" rendered="#{AccountsController.rowsPerPage == '332'}" required="true" />
But for some reason the code is not working. Can you help me to find the problem. Also I want to update AJAX functionality when I select number from the h:selectOneMenu list AJAX call to update the form.

There are 2 problems.
First, as JavaScript runs in the client side, you can't ajax-update a JSF component which isn't rendered to the client side. There's then simply nothing in the HTML DOM tree which JavaScript can select, manipulate and replace. You should instead ajax-update a JSF component which is always rendered to the client side.
<h:panelGroup id="customrowperpage">
<h:inputText value="#{AccountsController.rowsPerPage}" required="true"
rendered="#{AccountsController.rowsPerPage == '332'}" />
</h:panelGroup>
See also Why do I need to nest a component with rendered="#{some}" in another component when I want to ajax-update it?
Second, an Integer never equals a String. You're in the rendered attribute testing Integer rowsPerPage against String '332'. Remove those singlequotes to make it a fullworthy number.
<h:panelGroup id="customrowperpage">
<h:inputText value="#{AccountsController.rowsPerPage}" required="true"
rendered="#{AccountsController.rowsPerPage == 332}" />
</h:panelGroup>

Related

Performing data binding during ajax-request

I have the following markup:
<rich:panel id="selectorPanel">
<h:inputText value="#{myBean.field1}" />
<h:inputText value="#{myBean.field2}" />
<h:inputText value="#{myBean.field3}" />
<!-- and so forth -->
<h:selectOneMenu id="selector" value="#{myBean.selected}">
<a4j:ajax event="change" listener="#{myBean.doUpdateValues()}" render="selectorPanel" />
<f:selectItem itemLabel="#{msgs['bundle.addFilter']}" itemValue="#{null}" value="#{null}"/>
<f:selectItems value="#{myBean.filters}" />
</h:selectOneMenu>
</rich:panel>
The problem: when I change the value of the selectOneMenu the selectorPanel is being rerendered, therefore the values of the inputTexts are being refreshed and set to the myBean's properties value.
But I need to set values entered by user on the web-page to the corresponding bean's properties before rerendering.
Is it possible? How?
Why to do need to execute all? the #form should be enough or even list of inputs ids.
I've found the solution just now and we can actually use execute attribute as follows:
execute="#all"
It works for me.

Validate two components with dynamic id

Here is what I am trying to do:
I have a form with multiple pairs of input fields and a save button.
When the save button is pressed then for each pair of input fields should be validated if they are numbers and if the left value is smaller than the right value.
If not the validation errors will be shown and if it is the case a dialog will pop up to ask for the name under which it should be saved.
Here is what I got so far:
<h:form>
<ui:repeat value="#{myBean.someList}" var="elem">
<h:inputText id="min#{elem.id}" value="#{elem.minimum}" />
<h:inputText id="max#{elem.id}" value="#{elem.maximum}" />
<br />
<h:message for="min#{elem.id}" style="color:red" />
<h:message for="max#{elem.id}" style="color:red" />
</ui:repeat>
<h:commandButton value="save">
<f:ajax execute="#form" render="#form updateValidationFailedFlag"
onevent="
function(ajaxResult){
if(ajaxResult.status=='success' && !globalValidationFailedFlag)
showSaveDialog();
}"
/>
</h:commandButton>
<h:outputScript id="updateValidationFailedFlag">
globalValidationFailedFlag = #{facesContext.validationFailed};
</h:outputScript>
</h:form>
This works but doesn't check if the minimum is smaller then the maximum.
It will validate the input (checks if the input are integer) and shows the save dialog if no validation error occured.
To check if the minimum is smaller then the maximum I tried to follow the tutorial at http://www.mkyong.com/jsf2/multi-components-validator-in-jsf-2-0/
Variant 1 adds a listener after the validation is done and is able to add error messages that are shown in the browser. But that doesn't count as a validation failure and doen't set the facesContext.validationFailed flag.
Variant 2 writes a custom validator for one component and gives the other component as parameter to that validator. That would look something like this:
<f:validator validatorId="myValidator" />
<f:attribute name="maximum" value="#{max#{elem.id}}" />
That is not really valid EL and I don't know how to write it correctly.
What can I do to validate if each of those min-max pairs is valid
You don't need #{elem.id} here. The <ui:repeat> already takes care of that. It would be evaluated to an empty string anyway when JSF needs to set the id attribute.
<ui:repeat value="#{myBean.someList}" var="elem">
<h:inputText id="min" value="#{elem.minimum}" />
<h:inputText id="max" value="#{elem.maximum}" />
<br />
<h:message for="min" style="color:red" />
<h:message for="max" style="color:red" />
</ui:repeat>
As to the validation, just pass the value of the other component along:
<h:inputText id="min" binding="#{min}" value="#{elem.minimum}" />
<h:inputText id="max" value="#{elem.maximum}">
<f:validator validatorId="myValidator" />
<f:attribute name="min" value="#{min.value}" />
</h:inputText>
Note that I moved the validator to the second component. Otherwise it would be still invoked if the second component didn't pass conversion.
If you happen to use JSF utility library OmniFaces, then you could use its <o:validateOrder> instead.
<h:inputText id="min" value="#{elem.minimum}" />
<h:inputText id="max" value="#{elem.maximum}" />
<o:validateOrder components="min max" showMessageFor="min" />
See also:
How to use EL with <ui:repeat var> in id attribute of a JSF component
JSF doesn't support cross-field validation, is there a workaround?

Update another component based on a change in the selectonemenu value

We are using JSF 1.2 for our project. Requirement is to update another selectonemenu options based on a value change in the current selectonemenu.
Based on select1 option, select2 needs to be updated.
<h:selectOneMenu id="select1" value="#{subscription.subscriptions}" onchange="javascriptMethod()">
<f:selectItem id="item1" itemLabel="News" itemValue="1" />
<f:selectItem id="item2" itemLabel="Sports" itemValue="2" />
</h:selectOneMenu>
<h:panelGroup id="panel2">
<h:selectOneMenu id="select2" value="#{subscription.subscriptions2}">
<f:selectItem id="item1" itemLabel="News" itemValue="1" />
<f:selectItem id="item2" itemLabel="Sports" itemValue="2" />
</h:selectOneMenu>
</h:panelGroup>
You can use ajax support like this
<h:selectOneMenu id="select1" value="#{subscription.subscriptions}" >
<f:selectItem id="item1" itemLabel="News" itemValue="1" />
<f:selectItem id="item2" itemLabel="Sports" itemValue="2" />
</h:selectOneMenu>
Jsf 1.2 doesn't have ajax support the only thing you can do is full page reload which happens on a command button.
I presumed you are using richfaces as a third party for ajax and rich ui components.
Hope this helps.
If you are using primefaces then you can replace tag with like this
<p:ajax event="onchange" update="select2" action="if you want to anything in
backend"/>
If you want to do by javascript then have a hidden h:commandButton and put code in your
javascriptMethod() method like this
document.getElementById("myHiddenButtonId").click();

Composite Component - AJAX clientBehaviour

We have build a component that consists of a TextInput and Select Box. So that the selection can be made either via the drop down list or by typing in the value into the text field. When a selection is made the Input Box is automatically filled with the key and if the key is entered the correct item in the selection box is displayed.
<h:panelGroup rendered="#{cc.attrs.rendered}">
<div id="#{cc.clientId}" class="abc-select #{cc.attrs.styleClass}" >
<h:inputText id="input" disabled="#{cc.attrs.disabled}"
value="#{cc.attrs.value}">
<a4j:ajax event="change" render="select" />
</h:inputText>
<rich:select disabled="#{cc.attrs.disabled}"
id="select" value="#{cc.attrs.value}"
listWidth="#{cc.attrs.listWidth}"
converter="#{cc.attrs.converter}">
<f:selectItems value="#{cc.attrs.items}" var="item"
itemValue="#{item}" itemLabel="#{item.name}" />
<a4j:ajax event="selectitem" render="input" oncomplete="setFocusOnInputField();" />
</rich:select>
</div>
</h:panelGroup>
However when we pass in an <a4j:ajax event="change" ... /> with an onChange event to render a component external element. It only works when the selection is made via the drop down menu.
<abc:myComponent id="idMC"
value="#{bean.value}"
items="#{bean.itemList}"
converter="#{itemConverter}">
<a4j:ajax event="change" render="idA idB idC" />
</kfn:select>
So far I figured out that it must have to do with the fact that the inputText already has an a4j element listing for change <a4j:ajax event="change" render="select" />.
When I change the ajax element in the inputText element to use <f:ajax .. /> instead it works fine.
Can someone explain why this is the case and is there a better solution?

Ajax event within dataTable which is re-rendered by another ajax event does not execute

I am building an invoice application. When the user selects the client, then the services associated with the client are re-rendered in a dataTable by an ajax event. But, the ajax event of the selectOneMenu in the dataTable does not fire.
This is the form which lets the user select a client:
<h:form>
<h:selectOneMenu id="clientSelect" value="#{invoiceBean.client}">
<f:ajax execute="clientSelect" listener="#{invoiceServiceListener.processClientValueChange}" render=":test :invoiceData"/>
<f:selectItems value="#{invoiceBean.clientOptions}"/>
</h:selectOneMenu>
</h:form>
This will fire an ajax event which fills the services array in the invoiceBean and re-renders the dataTable which displays the services in another selectOneMenu in the dataTable. This part works properly.
This is the datatable that is re-rendered after selecting the client by the above form:
<h:panelGroup id="test">
<h:dataTable id="invoiceData" value="#{attributes.priceAttributes}" var="loop">
<h:column>
<ui:param name="service" value="service#{loop}" />
<ui:param name="description" value="description#{loop}" />
<ui:param name="price" value="price#{loop}" />
<h:form id="invoiceSelectMenu">
<h:selectOneMenu id="selectMenu" value="#{invoiceBean[service]}">
<f:ajax event="change" execute="selectMenu" listener="#{invoiceServiceListener.procesServiceValueChange}" render="#form" />
<f:attribute name="row" value="#{loop}" />
<f:selectItems value="#{invoiceBean.services}" />
</h:selectOneMenu>
<h:inputText id="description" value="#{invoiceBean[description]}" />
<h:inputText id="price" value="#{invoiceBean[price]}" />
<h:outputText value="#{loop}" />
</h:form>
</h:column>
</h:dataTable>
</h:panelGroup>
The selectOneMenu is filled properly. However, the ajax event in this select one menu does not work after it has been re-rendered by the first form. But, if I manually fill the services array without submitting the first form, then this ajax event executes normally. By the way, this ajax event also sets a value to the description and price input fields, which should be re-rendered when the event happens. The invoiceBean is request scoped.
This is going to be tricky. Due to JSF spec issue 790, whenever you re-render some content containing another <h:form> using <f:ajax>, then its view state will get lost. To solve this, you'd basically need to reference the full client ID of another <h:form> inside <f:ajax> instead of some containing component. But you've basically multiple forms inside a <h:dataTable> which can't be referenced by an absolute client ID.
You'd need to rearrange the code to put the <h:form> outside the <h:dataTable> and change the <f:ajax> to execute only the necessary components.
<h:form id="invoiceDataForm">
<h:dataTable id="invoiceData" value="#{attributes.priceAttributes}" var="loop">
<h:column>
<ui:param name="service" value="service#{loop}" />
<ui:param name="description" value="description#{loop}" />
<ui:param name="price" value="price#{loop}" />
<h:selectOneMenu id="selectMenu" value="#{invoiceBean[service]}">
<f:ajax execute="selectMenu,description,price" listener="#{invoiceServiceListener.procesServiceValueChange}" render="#form" />
<f:attribute name="row" value="#{loop}" />
<f:selectItems value="#{invoiceBean.services}" />
</h:selectOneMenu>
<h:inputText id="description" value="#{invoiceBean[description]}" />
<h:inputText id="price" value="#{invoiceBean[price]}" />
<h:outputText value="#{loop}" />
</h:column>
</h:dataTable>
</h:panelGroup>
and then reference it form the first form as follows:
<f:ajax execute="clientSelect" listener="#{invoiceServiceListener.processClientValueChange}" render=":invoiceDataForm" />
Note that some component libraries such as PrimeFaces have already workarounded/solved this in the component library specific ajax engine. So if it might happen that you're already using PrimeFaces, you could also replace <f:ajax> of the first form by <p:ajax>.
Unrelated to the concrete problem, the <f:attribute name="row" value="#{loop}" /> may not do what you expect it does. It will set null because <f:xxx> tags run during view build time, not during view render time tag. The #{loop} is not available during view build time. But that's subject for a different question :)

Resources