Component ID in ui:repeat problems - ajax

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.

Related

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

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'....

h:panelGroup not rendered when change the value of h:selectOneMenu using AJAX

I have a grid group that should be rendered when change the value of the selectOneMenu. I have attached a valueChangeListener to the selectOneMenu and it access succefully the function assigned to it , but the html component does not rerender. the strange thing that the flag the determines the rendering is called anyway.
<h:selectOneMenu value="#{channelsAdmin.profileID}" id="profileDrp" valueChangeListener="#{channelsAdmin.setProfileInit}">
<f:selectItems value="#{channelsAdmin.profiles}" var="Profile"
itemLabel="#{Profile.profileName}"
itemValue="#{Profile.profileId}" />
<f:ajax execute="#this" immediate="true" render="pnl"></f:ajax>
</h:selectOneMenu>
<h:panelGroup id="pnl" rendered="#{channelsAdmin.ccpAuthPanelFlage}">
<h:inputText id="sest" value="hew">
</h:inputText>
</h:panelGroup>
Bean:
public void setProfileInit(ValueChangeEvent e) {
ccpAuthPanelFlage=true;
ccpPurchPanelFlage=true;
}
The <f:ajax render="pnl"> works roughly as follows:
When the ajax response is arrived, obtain the update element with id="pnl" from XML response.
Now obtain the element with id="pnl" from HTML document by document.getElementById().
Replace its contents with the update element from XML response.
In your particular case, step 2 failed because there's no such element in the HTML document. It doesn't exist in the HTML document because it's never been rendered in first place.
You need to make sure that <f:ajax render="..."> refers an element which is always rendered so that JavaScript can find it by document.getElementById() in order to replace its contents.
<h:selectOneMenu value="#{channelsAdmin.profileID}" id="profileDrp" valueChangeListener="#{channelsAdmin.setProfileInit}">
<f:selectItems value="#{channelsAdmin.profiles}" var="Profile"
itemLabel="#{Profile.profileName}"
itemValue="#{Profile.profileId}" />
<f:ajax execute="#this" immediate="true" render="pnl"></f:ajax>
</h:selectOneMenu>
<h:panelGroup id="pnl">
<h:panelGroup rendered="#{channelsAdmin.ccpAuthPanelFlage}">
<h:inputText id="sest" value="hew">
</h:inputText>
</h:panelGroup>
</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?

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>

How to render a component outside datatable by ajax?

I have a <h:dataTable> with a <h:commandLink> in a column and a <h:outputext> outside the <h:dataTable> which I need to render by the command link.
<h:form>
<h:dataTable value="#{userControl.lista}" var="c">
<h:column>
<f:facet name="header" >
<h:outputText styleClass="leftColumn" value="Nombre"/>
</f:facet>
#{c.nombre}
</h:column>
<h:column>
<f:facet name="header" >
Usuario
</f:facet>
<h:commandLink actionListener="#{userControl.setAdmin_user(c.user)}" value="#{c.user}">
<f:ajax render="output" />
</h:commandLink>
</h:column>
</h:dataTable>
<h:outputText id="output" value="#{userControl.admin_user}"/>
</h:form>
It does not work. If I move the <h:outputText> inside the same column, then it works.
How is this caused and how can I solve it?
The client ID as you currently have in the <f:ajax render> does not start with the default NamingContainer separator character : and is therefore relative to the current NamingContainer component, which is in your case the <h:dataTable>. So it will work only when the outputtext component is also in the same datatable.
You need to refer the outputtext component by an absolute client ID instead. For starters who do not have memorized all NamingContainer components yet, the easiest way to find it out is to check the id attribute of the generated HTML element in the webbrowser. Open the page in webbrowser and do View Source and locate the <span> element generated by <h:outputText id="output"> and take exactly this ID and prefix it with : to make it absolute to the view root.
E.g.
<span id="formId:output">
where formId is the ID of any <h:form> for the case the outputtext is enclosed in a <h:form>. If you don't have specified a fixed ID for the <h:form>, then JSF will autogenerate one. You'd like to specify a fixed ID then like <h:form id="formId">.
So the <f:ajax render> should look like this then
<f:ajax render=":formId:output" />

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

Resources