This is the general structure of the jsf page:
<ui:repeat id="repeater" varStatus="stat" >
<h:form id="the-form">
<h:panelGroup id="renderme">
<ui:repeat id="inner-repeater">
<h:commandButton>
<f:ajax render=":repeater:#{stat.index}:the-form:renderme">
</h:commandButton>
</ui:repeat>
</h:panelGroup>
</h:form>
</ui:repeat>
So.. the button here, when clicked, should cause the element renderme to be re-rendered.
In practice, I get "component with id ... not found" although when I look at the html page, the generated id is correct.
Moreover, I tried to use #{component.parent.parent.clientId} which produced the same id and still got the same error message from JSF.
Any idea on why this is failing?
Thanks!
This doesn't work for the simple reason because viewRoot.findComponent("repeater:0:the-form:renderme") as requested by <f:ajax render> doesn't return anything. Such a component does not exist in the component tree. This ID only exists in the generated HTML output. Instead, it's viewRoot.findComponent("repeater:the-form:renderme") which would return something, but this does in turn not exist in HTML DOM tree where JavaScript needs to do the update based on ajax response. Even then, this is not exactly what you need.
This is where JSTL can come to rescue. It's capable of dynamically building the JSF component tree and of dynamically assigning IDs to components generated in a loop:
<c:forEach ... varStatus="stat">
<h:form id="the-form_#{stat.index}">
<h:panelGroup id="renderme">
<ui:repeat id="inner-repeater" ...>
<h:commandButton ...>
<f:ajax render=":the-form_#{stat.index}:renderme">
</h:commandButton>
</ui:repeat>
</h:panelGroup>
</h:form>
</c:forEach>
This will only cause problems when you bind <c:forEach> value to a view scoped bean and you use Mojarra version older than 2.1.18. You'd need to upgrade to at least Mojarra 2.1.18 then.
See also:
JSTL in JSF2 Facelets... makes sense?
Related
I have a button to submit a form and invoke a managed bean action.
<h:form>
...
<h:commandButton value="Submit" action="#{bean.submit}" />
</h:form>
But when I press the button it refreshes the whole page and sometimes also changes the URL.
Is there some way to not refresh the page and still invoke the action?
Make use of Ajax. It's a matter of nesting <f:ajax> inside the command button of interest.
<h:form>
...
<h:commandButton ...>
<f:ajax execute="#form" render="#none" />
</h:commandButton>
</h:form>
Particularly the render="#none" (which is the default value anyway, you could just omit the attribute altogether) will instruct JSF to re-render just nothing after the submit. If you intend to re-render only a specific component instead of the whole page, then you could also specify the ID of that specific component in render attribute.
<h:form>
...
<h:commandButton ...>
<f:ajax execute="#form" render="result" />
</h:commandButton>
...
<h:panelGroup id="result">...</h:panelGroup>
</h:form>
If you're already using PrimeFaces, then it's a matter of simply using <p:commandButton> instead of <h:commandButton>. It uses by default ajax already, so you don't need to nest in a <f:ajax>. You only need to remember to use attribute names process and update instead of execute and render.
<h:form>
...
<p:commandButton ... update="result" />
...
<h:panelGroup id="result">...</h:panelGroup>
</h:form>
The execute attribute defaults to #form already, so it could be omitted.
See also:
Understanding PrimeFaces process/update and JSF f:ajax execute/render attributes
How to find out client ID of component for ajax update/render? Cannot find component with expression "foo" referenced from "bar"
Why do I need to nest a component with rendered="#{some}" in another component when I want to ajax-update it?
So I've found a few answers close to this, and I've found enough to fix the problem I had. But even so, I'm curious as to understand the workings around this. Let me illustrate with an example :
I have a facelet .xhtml page that looks like this (shortned).
<h:form id="resultForm">
<h:panelGroup class="search_form" layout="block">
<h:inputText id="lastname" value="#{search.lastname}"/>
<h:commandButton action="#{search.find}" value="Find">
<f:ajax execute="lastname" render="resultDisplay"/>
</h:commandButton>
</h:panelGroup>
<h:dataTable value="#{search.searchResults}" var="results" id="resultDisplay"
rendered="#{!empty search.searchResults}">
<h:column>
#{results.field}
</h:column>
</h:dataTable>
</h:form>
Now, for the sake of breivity, I will not post all the backing bean code, but I have something of this sort :
public void find() {
searchResults = setResults(true);
}
Where searchResults is an ArrayList<Objects>. After a search, it is not null - checked in multiple tests (can be null, but not in the testing I am doing).
Now. This does NOT work.
But if I nest the dataTable inside another, let's say panelGroup, it will work.
<h:panelGroup id="resultDisplay">
<h:dataTable value="#{search.searchResults}" var="results"
rendered="#{!empty search.searchResults}">
<h:column>
#{results.field}
</h:column>
</h:dataTable>
</h:panelGroup>
Now, this changes allows everything to work fine. I'd be okay with this... but I guess I am seeking a bit of understanding as well. Any insight as to why I have to nest these components? I am surely missing something!
Ajax updating is performed by JavaScript language in the client side. All which JavaScript has access to is the HTML DOM tree. If JSF does not render any component to the HTML output, then there's nothing in the HTML DOM tree which can be obtained by JavaScript upon Ajax update. JavaScript cannot get the desired element by its ID.
It will only work if you wrap the conditionally JSF-rendered component in another component which is always rendered to the HTML output and thus always present in the HTML DOM tree and thus always obtainable by JavaScript. Reference that wrapper component instead during ajax render/update.
See also:
Communication in JSF2 - Ajax rendering of content which is by itself conditionally rendered
I have a problem with submitting composite components.
Most of my composite components contain both the input components and the "submit" button.
When I tried to put the button still in the same h:form but not in the same composite component, the submitted value seemed to be "lost" somewhere. And, for instance, my validators got called on original values.
Example :
<composite:interface>
<composite:attribute name="titreContext" required="true"/>
</composite:interface>
<composite:implementation>
<p:outputPanel id="selectionTitreDetailsPanel" styleClass="selectionTitreDetails">
<p:outputPanel id="selectionTitreDetailsPanelInner" rendered="#{not empty cc.attrs.titreContext.selected}">
<p:panelGrid columns="2" id="panelId">
<h:outputText id="idLabel" value="Id :"/>
<h:outputText id="id" value="#{cc.attrs.titreContext.selected.titeluid}"/>
<p:tooltip for="id" value="Identifiant unique"/>
</p:panelGrid>
<p:panelGrid columns="2" id="titelePanel">
<p:outputLabel for="selectTitele" value="Titre :"/>
<p:selectOneMenu id="selectTitele" value="#{cc.attrs.titreContext.selected.titele}" effect="fold" styleClass="fullWidth">
<f:selectItems value="#{constants.getTitelesForTypman(cc.attrs.titreContext.selected.titele.typman)}" var="titele" itemLabel="#{titele.titelelil}" itemValue="#{titele}" styleClass="fullWidth"/>
<p:column styleClass="fullWidth">#{titele.titelelil}</p:column>
</p:selectOneMenu>
</p:panelGrid>
[...]
<p:commandButton id="confirmerModifications" icon="small_edit" type="submit" value="Confirmer les modifications"
action="#{elutersEditionContext.confirmeModifsSelection}" process="mandatsTerritorial"
update="mandatsTerritorial #{cc.attrs.notifUpdates}"/>
</composite:implementation>
works.
But putting the p:commandButton out of the composite :
<h:form>
<mylib:mycomponent /*parameters *//>
<p:commandButton /*parameters*/ />
</h:form>
does not work. When I debug my validators, I can see that the modified values where not even submitted. Neither getLocalValue, getSubmittedValue nor getValue is changed.
Is there a syntax in composite component declaration to use to correct this situation ?
By the way : when I was writing my components as composite components rather than custom components, retrieving #{asen} in the backing bean just worked.
Thanks in advance.
I am using :
PrimeFaces 3.4.1
CODI 1.0.5
OpenWebBeans 1.1.6
MyFaces 2.1.9
Tomcat 7.0.32
(update) This very strange problem was caused by h:form nesting.
Very strange because h:form nesting did not perturbate the processing of the first level of composite components, but caused this strange "input lost" in nested composite.
Nesting looked like this :
<h:form>
...
<p:tabView ...>
<p:tab>
<h:form>
<my:composite ....>
</h:form>
</p:tabView>
</h:form>
You're using a relative client ID in the process attribute of the <p:commandButton>:
<p:commandButton ... process="mandatsTerritorial" />
A relative client ID is relative to the parent NamingContainer component. It will be searched as direct child of the NamingContainer component. If the child is by itself a NamingContainer, then its children would not be searched.
Composite components are by itself in fact also NamingContainer components. If the button is placed in the composite, then this will be searched as direct child of the <cc:implementation>. In your particular case, only the component with id="mandatsTerritorial" will be processed on form submit, including all of its children (note that this component is nowhere visible in the code posted so far, but I'd imagine that you omitted it for brevity).
If the button is placed in <h:form>, then this will be searched as direct child of the <h:form>. However as this is apparently been placed inside the composite (which is, as said, another NamingContainer component), it wouldn't be found and hence basically nothing would be processed. You'd need to fix the process to point to the right client ID. E.g.
<h:form>
<mylib:mycomponent id="mycomponent" />
<p:commandButton ... process="#this mycomponent:mandatsTerritorial" />
</h:form>
This way it will process itself (mandatory to invoke the action!) and the component with id="mandatsTerritorial" inside the <cc:implementation> of the composite with id="mycomponent".
As a completely different alternative, which would work just fine in this particular construct, is to remove the process attribute altogether. It defaults to #form already which will thus process the entire form.
Update as per your question update: nesting forms is invalid in HTML. Using the JSF <h:form> representation doesn't change that; you'd still end up with nested forms in HTML. The browser behaviour is unspecified as to which data would be submitted to the server. Make sure that you don't nest <h:form> in JSF as well.
I have the following piece of code:
<h:outputText value="#{lecture.lectureName}" />
<c:forEach items="#{criterionController.getCriteriaForLecture(lecture)}" var="criterion">
<h:outputText value="#{criterion.criterionName}" />
<h:commandLink value="Edit"/>
<h:commandLink value="Delete"/>
</c:forEach>
The output text part is working perfectly and displays what it should display so this proves that the lecture object is set. However the for each tag gives a null pointer exception. When I debugged the code, I saw that the lecture object was taken as null when the method getCriteriaForLecture() was called.
How can this behaviour explained?
This can happen if the lecturer variable is in turn been set by a JSF iterating component such as <h:dataTable>, <ui:repeat>, etc or probably a <p:tabView>, based on your previous question.
A more detailed explanation of this behaviour can be found here: JSTL in JSF2 Facelets... makes sense? To the point, JSTL tags runs during building the view, not during rendering the view. The lecturer variable is in your particular case only available during rendering the view and is thus always null during building the view, when JSTL runs.
To solve it, use a normal JSF component like <ui:repeat> instead.
<ui:repeat value="#{criterionController.getCriteriaForLecture(lecture)}" var="criterion">
<h:outputText value="#{criterion.criterionName}" />
<h:commandLink value="Edit"/>
<h:commandLink value="Delete"/>
</ui:repeat>
Much better would be not doing business actions in getters at all. Just make the List<Criterion> a property of Lecture instead.
<ui:repeat value="#{lecture.criterions}" var="criterion">
<h:outputText value="#{criterion.criterionName}" />
<h:commandLink value="Edit"/>
<h:commandLink value="Delete"/>
</ui:repeat>
See also Why JSF calls getters multiple times
So I've found a few answers close to this, and I've found enough to fix the problem I had. But even so, I'm curious as to understand the workings around this. Let me illustrate with an example :
I have a facelet .xhtml page that looks like this (shortned).
<h:form id="resultForm">
<h:panelGroup class="search_form" layout="block">
<h:inputText id="lastname" value="#{search.lastname}"/>
<h:commandButton action="#{search.find}" value="Find">
<f:ajax execute="lastname" render="resultDisplay"/>
</h:commandButton>
</h:panelGroup>
<h:dataTable value="#{search.searchResults}" var="results" id="resultDisplay"
rendered="#{!empty search.searchResults}">
<h:column>
#{results.field}
</h:column>
</h:dataTable>
</h:form>
Now, for the sake of breivity, I will not post all the backing bean code, but I have something of this sort :
public void find() {
searchResults = setResults(true);
}
Where searchResults is an ArrayList<Objects>. After a search, it is not null - checked in multiple tests (can be null, but not in the testing I am doing).
Now. This does NOT work.
But if I nest the dataTable inside another, let's say panelGroup, it will work.
<h:panelGroup id="resultDisplay">
<h:dataTable value="#{search.searchResults}" var="results"
rendered="#{!empty search.searchResults}">
<h:column>
#{results.field}
</h:column>
</h:dataTable>
</h:panelGroup>
Now, this changes allows everything to work fine. I'd be okay with this... but I guess I am seeking a bit of understanding as well. Any insight as to why I have to nest these components? I am surely missing something!
Ajax updating is performed by JavaScript language in the client side. All which JavaScript has access to is the HTML DOM tree. If JSF does not render any component to the HTML output, then there's nothing in the HTML DOM tree which can be obtained by JavaScript upon Ajax update. JavaScript cannot get the desired element by its ID.
It will only work if you wrap the conditionally JSF-rendered component in another component which is always rendered to the HTML output and thus always present in the HTML DOM tree and thus always obtainable by JavaScript. Reference that wrapper component instead during ajax render/update.
See also:
Communication in JSF2 - Ajax rendering of content which is by itself conditionally rendered