<f:ajax> reload sibling within <ui:repeat> - ajax

this is what I have
<h:body>
<ui:repeat id="repeatBlockId" var="dataItem" value="#{backingBean.dataList}">
<custom:myComp id="myCompId" name="#{dataItem.name}"/>
</ui:repeat>
</h:body>
and here the composite component
<composite:implementation xmlns:custom="http://java.sun.com/jsf/composite/components">
<ui:param name="name" value="#{cc.attributes.name}"/>
<h:panelGroup id="toBeRefreshedId">
#{backingBean.randomNumber}
</h:panelGroup>
<h:form id="myFormId">
<br/>#{name}
<h:commandButton>Refresh
<f:ajax render=":#{cc.clientId}:toBeRefreshedId" />
</h:commandButton>
</h:form>
</composite:implementation>
Whatever I try, I don't get it done to update the sibling of the form. I always get the following error message:
<f:ajax> contains an unknown id ':repeatBlockId:0:myCompId:toBeRefreshedId' - cannot locate it in the context of the component j_idt6
I also tried to simply refresh the whole composite block with <f:ajax render=":#{cc.clientId}" /> resulting in the same error message ...unknown id ':repeatBlockId:0:myCompId'....

Related

JSF f:ajax listener not called when used with composite component and execute="#this"

I have a following test composite component:
<composite:interface>
<composite:clientBehavior event="click" targets="#{cc.clientId}:label"
name="click" />
</composite:interface>
<composite:implementation>
<h:outputLabel value="click here" id="label" style="background-color: red" />
</composite:implementation>
and when I use it attaching f:ajax client behavior my listener method is not called on server side, but POST request reaches the server. I noticed that when I don't specify f:ajax's execute attribute or specify it as "#this" listener is not called, but with execute="#form" or "#all" it works.
These examples don't work:
<h:form>
<t:testComposite>
<f:ajax event="click" listener="#{bean.test}" />
</t:testComposite>
</h:form>
<h:form>
<t:testComposite>
<f:ajax event="click" listener="#{bean.test}" execute="#this"/>
</t:testComposite>
</h:form>
These examples work:
<h:form>
<t:testComposite>
<f:ajax event="click" listener="#{bean.test}" execute="#form" />
</t:testComposite>
</h:form>
<h:form>
<t:testComposite>
<f:ajax event="click" listener="#{bean.test}" execute="#all" />
</t:testComposite>
</h:form>
<h:form id="form">
<h:panelGroup id="group">
<t:testComposite>
<f:ajax event="click" listener="#{bean.test}" execute=":form:group" />
</t:testComposite>
</h:panelGroup>
</h:form>
I tested it on tomcat 7 with myfaces 2.1.12 and jboss as7 with mojarra 2.1.18.
My question is: why doesn't f:ajax work with execute="#this" when attached to a composite component?
The base of the targets is implicit, so targets="#{cc.clientId}:label" is not necessary. You should use targets="label" instead.
It is hard to explain, but the reason why targets="#{cc.clientId}:label" does not work is because the EL expression is calculated ouside the context or is evaluated before the component tree is built, so in this case for example, when it tries to derive the parent component, the child has not been attached to the view yet.
The general rule is don't call getClientId() when facelet is building the tree (inside a facelet TagHandler for example), and instead use a listener attached to PostAddToViewEvent for that.

JSF and AJAX: hiding part of site until first ajax request

I'm trying to create a page where some content will be displayed after ajax request. Here is part of my code:
<h:panelGroup>
<h:form>
Retrive object by id: <h:inputText id="myInput" value="#{myManager.id}"/>
<h:commandButton value="ok" action="#{myManager.getById}" >
<f:ajax execute="myInput" render="resultRow" />
</h:commandButton>
<h:panelGroup id="resultRow" >
<br />
You retrived object which id is:
<h:outputText value="#{retrivedObject.id}" />
and its name is:
<h:outputText value="#{retrivedObject.name}" />
</h:panelGroup>
</h:form>
</h:panelGroup>
My problem is taht the "You retrived object which id is: " is rendered even before I retrive any object. I want it to be hidden until I click my command button. Is it possible to do only with jsf + html (this have to be done with ajax)? I could use some javascript if I will have to, but I prefer not.
I tried to solve this by rendered="#{!retrivedObject.id==0}" (my object can't have id 0), but this doesn't work - panel group isn't rendered at all and when I call ajax reqest it is unable to find "resultRow" id.
Thanks for help in advance.
It may not be exactly what you are looking for but you can always wrap the panelGroup in a div and set the div visibility to hidden, then use javascript to change the visibility when you click the command button.
You can use the rendered attribute like this :
<h:panelGroup>
<h:form>
Retrive object by id: <h:inputText id="myInput" value="#{myManager.id}"/>
<h:commandButton value="ok" action="#{myManager.getById}" >
<f:ajax execute="myInput" render="resultRow" />
</h:commandButton>
<h:panelGroup id="resultRow">
<h:panelGroup rendered="#{retrivedObject ne null}">
<br />
You retrived object which id is:
<h:outputText value="#{retrivedObject.id}" />
and its name is:
<h:outputText value="#{retrivedObject.name}" />
</h:panelGroup>
</h:panelGroup>
</h:form>
</h:panelGroup>

understanding ajax execute behavior with datatable using jsf 2.0

i'm trying to use an search button which brings back the selected items (can be more than one) and it updates the datatable. Then i have a selectBooleanCheckbox next to each colomn, when user selects "n" items then presses the Select the checked Items it insert DB.
The code can be seen below:
<h:panelGrid columns="5">
<h:form>
<h:outputText value="Item Name"/>
<p:inputText value="#{StockController.itemName}"/>
<h:commandButton value="Search" action="#{StockController.Search}">
<f:ajax execute="#form" render=":results"/>
</h:commandButton>
</h:form>
//The code sample below belongs to BalusC see the post here
<h:panelGroup id="results">
<h:form>
<h:dataTable value="#{bean.entities}" var="entity">
<h:column>
<h:selectBooleanCheckbox value="#{bean.checked[entity.id]}" />
</h:column>
...
</h:dataTable>
<h:commandButton value="Select the checked Items" action="#{StockController.insertDao}" >
<f:ajax execute="#form" render=":results"/>
</h:commandButton>
</h:form>
</h:panelGrid>
Now, i have read many blogs and Core javaServer Faces 3, i don't think there is logical error in ajax usage. I tested by removing each ajax then both works fine but whenever i try to use both of cummondButtons with ajax, the second one "Select the checked Items" does not even call the "StockController.insertDao" method.
Any help is appreciated.
Thanks all.
When you want to ajax-render content which in turn contains another form, then you need to include the ID of that form in the render attribute as well, otherwise the other form will lose its view state and nothing will be processed on submit.
<h:form>
...
<h:commandButton value="Search" action="#{StockController.Search}">
<f:ajax execute="#form" render=":results :resultsForm" />
</h:commandButton>
</h:form>
<h:panelGroup id="results">
<h:form id="resultsForm">
...
</h:form>
</h:panelGroup>
If there's however nothing which goes outside <h:form> inside the same <h:panelGroup>, then you can just omit the <h:panelGroup> altogether and re-render the form directly.
<h:form>
...
<h:commandButton value="Search" action="#{StockController.Search}">
<f:ajax execute="#form" render=":results" />
</h:commandButton>
</h:form>
<h:form id="results">
...
</h:form>
This is related to JSF spec issue 790.
See also:
Communication in JSF2 - Ajax rendering of content which contains another form

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

Component ID in ui:repeat problems

I'm trying to render a grandparent component.
The code:
<h:form prependId="false>
<h:panelGroup id="outer_panel">
<ui:repeat var="curr" value="#{bean.map}">
<h:panelGroup id="inner_panel">
<h:commandButton value="Remove" action="actionThing" >
<f:ajax render="outer_panel" />
</h:commandButton>
</h:panelGroup>
</ui:repeat>
</h:panelGroup>
</h:form>
I get an exception:
javax.faces.FacesException: Component with id:outer_panel not found
Tried to add the index to the id, which didn't work neither:
<h:form prependId="false>
<h:panelGroup id="outer_panel">
<ui:repeat var="curr" value="#{bean.map}" varStatus="loop">
<h:panelGroup id="inner_panel">
<h:commandButton value="Remove" action="actionThing" >
<f:ajax render="#{loop.index+1}:outer_panel" />
</h:commandButton>
</h:panelGroup>
</ui:repeat>
</h:panelGroup>
</h:form>
Any idea why the ID is not found?
Thanks.
It is not found because you used a relative client ID in render attribute. It becomes relative to the closest parent naming container component. In this case, that's the <ui:repeat> (it prepends generated client ID of the children with the row index). So the component with ID outer_panel has to be inside the <ui:repeat> in order to get it to work. However, the outer_panel is actually outside it.
You need to specify an absolute client ID instead of a relative client ID. To make it absolute (so that it will basically scan from the view root on), prefix the client ID with :.
<f:ajax render=":outer_panel" />
For the case that you didn't use prependId="false" on the <h:form>, then you should have used
<f:ajax render=":form_id:outer_panel" />
instead.

Resources