Why do I need to nest a component with rendered="#{some}" in another component when I want to ajax-update it? - jsf

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

Related

How to render input component inside dialog upon opening dialog and unrender when closing dialog [duplicate]

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

How do PrimeFaces Selectors as in update="#(.myClass)" work?

I don't understand how PrimeFaces selectors (PFS) work.
<h:outputText value="#{bean.text1}" styleClass="myClass" />
<p:commandButton update="#(.myClass)" />
I can use it. And I think it's a fantastic tool although it doesn't function always for me. The .myClass is client side jQuery selector. How does JSF on server side know what to update?
I can understand how normal JSF ID selectors work.
<h:outputText value="#{bean.text1}" id="textId" />
<p:commandButton update="textId" />
The textId references an ID of component in the component tree as defined in XHTML file in server side. So I can understand how JSF finds the right component.
But if you are using primefaces selectors, the client side jQuery selectors are used. How does JSF know which component has to be updated? Sometimes I have problems with PFS. It doesn't seem to function always for me. Is there something what you should keep in mind if you are using PFS?
You probably already know that PrimeFaces is using jQuery under the covers. PrimeFaces Selectors are based on jQuery. Anything which you specify in #(...) will be used as jQuery selector on the current HTML DOM tree. For any found HTML element, which has an ID, exactly this ID will ultimately be used in the update.
Basically, for a update="#(.myclass)", PrimeFaces will under the covers roughly do this:
var $elements = $(".myclass");
var clientIds = [];
$.each($elements, function(index, element) {
if (element.id) {
clientIds.push(":" + element.id);
}
});
var newUpdate = clientIds.join(" "); // This will be used as `update` instead.
So, in case of e.g.
<h:form id="formId">
<h:outputText id="output1" styleClass="myclass" ... />
<h:outputText styleClass="myclass" ... />
<h:outputText id="output3" styleClass="myclass" ... />
</h:form>
this command button update
<p:commandButton ... update="#(.myclass)" />
will end up with exactly the same effect as
<p:commandButton ... update=":formId:output1 :formId:output3" />
Note that this also works for autogenerated IDs. I.e. the <h:form id> is not mandatory.
Sometimes I have a problems with PFS. Is there something what you are should keep in mind if you are using PFS ?
It can happen that you selected "too much" (e.g. #(form) doesn't select current form, but all forms, exactly like $("form") in jQuery!), or that you actually selected nothing (when the desired HTML DOM element has actually no ID). Investigating element IDs in the HTML DOM tree and the request payload in the HTTP traffic monitor the should give clues.
The desired elements in the HTML DOM tree must have an (autogenerated) ID. The javax.faces.partial.render request parameter in the HTTP traffic monitor must contain the right client IDs. The element's rendered attribute in the JSF component tree must evaluate true during update. Etcetera.
In your particular example, the <h:outputText> won't end up in the generated HTML output with any ID. Assigning it an id should solve your problem with updating it.
So, this example won't work
<h:form>
<h:outputText value="#{bean.text1}" styleClass="myClass" />
<p:commandButton value="Update" update="#(.myClass)" />
</h:form>
but this example will work (note that assigning the form an ID is not necessary):
<h:form>
<h:outputText id="myText" value="#{bean.text1}" styleClass="myClass" />
<p:commandButton value="Update" update="#(.myClass)" />
</h:form>

Conditionally rendering an <ui:include>

I am trying to toggle a page that shows a <rich:dataTable>. Before I just included the <ui:include> template and it would just show the table the whole time.
<ui:include src="../log/viewDlg.xhtml"/>
Now I want to be able to toggle it on/off on the web-page. Showing on the page with maybe a button or link. How can I achieve it?
Update 1: I am unable to get it to show up for some odd reason, Here is what I wrote so far based on feed back
View:
<a4j:commandLink value="View"
action="#{bean.showview}" render="viewPanel"/>
<h:panelGroup id="viewPanel">
<h:panelGroup id="tableRenderPanel" rendered="#{bean.showPolicyView}">
<ui:include src="../log/viewDlg.xhtml"/>
</h:panelGroup>
</h:panelGroup>
Backing bean:
private boolean showPolicyView = false;
public void showView() {
showPolicyView = !showPolicyView;
}
public boolean isShowPolicyView(){
return showPolicyView;
}
Wrap your <ui:include> inside two <h:panelGroup> elements. There's a catch here, you can't rerender a conditional component. Why's this? because when the element's rendered attribute resolves to false, it will not be considered while rendering the view so it can't be the target of an operation (in this case, related to renderization).
Jumping to the code, you'll have this:
<h:panelGroup id="wrapperPanel">
<h:panelGroup id="tableRenderPanel" rendered="#{yourBean.renderTable}">
<ui:include src="../log/viewDlg.xhtml"/>
</h:panelGroup>
</h:panelGroup>
yourBean#renderTable is a Boolean property that determines if the component will be rendered. When it evaluates to false, the component is not included in the component tree.
Toggling the view
To toggle the view, simply create a bean method that either refreshes the page
<h:commandLink action="#{yourBean.toggleTableView}"/>
or the particular panel through AJAX. To do this in JSF 1.2, rely on extensions like RichFaces to introduce AJAX, if you can. For example, should you choose RichFaces, you can use <a4j:commandLink/> and its handy render (or reRender in older versions) attribute to achieve what you could do normally with an <f:ajax/> in JSF 2
<a4j:commandLink action="#{yourBean.toggleTableView}" reRender="wrapperPannel"/>
Or, another alternative is
<a4j:commandLink action="#{yourBean.toggleTableView}">
<a4j:support event="oncomplete" reRender="wrapperPannel"/>
</a4j:commandLink>
Please note that the reRender attribute may vary depending on the structure of your page, but it should always reference the id of the wrapping panel in the end. Also, reRender was renamed to simply render in late RichFaces versions.
So, assuming you have a renderTable property (getter + setter) in yourBean, the toggleTableView must change it, in order to dinamically define if the component is to be rendered or not (renderTable = false).
Introducing RichFaces
Check this link for help in setting up RichFaces in your project.
I like the use of ui:include better than inserting h:panelBoxes like here:
<ui:fragment rendered="#{myBean.yourCondition()}">
<ui:include src="viewA.xhtml"/>
</ui:fragment>
<ui:fragment rendered="#{not myBean.yourCondition()}">
<ui:include src="viewB.xhtml"/>
</ui:fragment>
Advantage: Tag handlers do not represent components and never become a part of the component tree once the view has been built. It won't interefere with your CSS - the h:panelBox, in contrary, inserts a div or span.
... Another approach would be c:choose, which works but can cause render phases issues.
<c:choose>
<c:when test="#{myBean.yourCondition()}">
<ui:include src="viewA.xhtml"/>
</c:when>
<c:otherwise>
<ui:include src="viewB.xhtml"/>
</c:otherwise>
</c:choose>
Caution: When fiddling with tag handlers (like any c:xxx), be sure to know the difference between UI Components and Tag Handlers. Namely that UI Components and Tag Handlers are renderend in different phases. That implies that you cannot create a variable in a composite component and use it in a nested tag handler. c:choose and ui:include are both tag handlers, so normally it's not a problem. Read the link, it's a very short example and very insightful.

Trying to render a h:panelGroup outside ui:repeat with complicated structure

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?

commandButton inactive after ajax rendering

I have a problem with these two commandButton : Join and Leave.
I want to hide Join if I click on leave and vice-versa.
When I put ajax on false, there is no problem (but all the page is refresh and I don't find this optimal).
But when ajax attribut is on true with specific updating (cf comment in the code), the rendering is good but the new button whitch appear become inactive. If I click on it, nothing happens (well it's seems the actionListener trigger but the view is not refreshed, I have to manual refresh to see the difference)
Thanks for reading.
<h:form id="formWaitingList" rendered="#{connexion.connected}" >
<p:commandButton id="Join"
actionListener = "#{connexion.joinWaitingList()}"
rendered="#{!connexion.waiting}"
ajax="false"
<!-- ajax="true"
update="Join,Leave"-->
value="Join"/>
<p:commandButton id="Leave"
value="Leave"
ajax="false"
<!-- ajax="true"
udpate="Join,Leave"-->
rendered="#{connexion.waiting}"
actionListener ="#{connexion.leaveWaitingList()}" />
</h:form>
It seems that you're not entirely familiar with HTML/JavaScript. You know, JSF is basically a HTML/JavaScript(/CSS) code generator. Ajax updating works basically like this in JavaScript:
After sending the ajax request to JSF via XMLHttpRequest, retrieve a XML response which contains all elements which needs to be updated along with their client IDs.
For every to-be-updated element, use document.getElementById(clientId) to find it in the current HTML DOM tree.
Replace that element by new element as specified in ajax XML response.
However, if a JSF component has not generated its HTML representation because of rendered="false", then there's nothing in the HTML DOM tree which can be found and replaced. That totally explains the symptoms you're "seeing".
You basically need to wrap conditionally rendered JSF components in a component whose HTML representation is always rendered and then reference it instead in the ajax update.
For example,
<h:form>
...
<h:panelGroup id="buttons">
<p:commandButton ... update="buttons" rendered="#{condition}" />
<p:commandButton ... update="buttons" rendered="#{not condition}" />
</h:panelGroup>
</h:form>
See also:
Why do I need to nest a component with rendered="#{some}" in another component when I want to ajax-update it?

Resources