How to render input component inside dialog upon opening dialog and unrender when closing dialog [duplicate] - 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

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?

render richfaces popupPanel after bean method is finished

I have a simple problem where I want to display a popup if something went wrong in my managed bean.
The bean holds a list of exceptions that can be raised, with getter/setter methods.
The xhtml looks like this
<rich:panel>
<h:form>
<a4j:commandButton value="Compute Mission"
action="#{missionHandler.generateMissionFeasability}"
render="popupPanel">
</a4j:commandButton>
</h:form>
</rich:panel>
<rich:popupPanel id="popupPanel" modal="true" autosized="true"
resizeable="false" moveable="false" rendered="#{not empty missionHandler.exceptions}">
<f:facet name="header">
<h:outputText value="Exceptions raised during the processing " />
</f:facet>
<f:facet name="controls">
<h:outputLink value="#"
onclick="#{rich:component('popupPanel')}.hide();return false;">
</h:outputLink>
</f:facet>
</rich:popupPanel>
As you see I have a command button that should call generateMissionFeasibility method in the bean.
The method will (among other things) add exception in the exceptions list.
I would like to check the list (if it's empty or not) to display the popup
The code above doesn't work because I think the popup is rendered before the end of the method in the bean, and the list is empty at the beginning.
One way to make popup panel be shown once rendered is to change
rendered="#{not empty missionHandler.exceptions}"
to
show="#{not empty missionHandler.exceptions}"
The code doesn't work because the first time the view is about to be rendered, missionHandler.exceptions will be empty, which means popupPanel never makes it to the browser. Subsequent requests to reRender popupPanel will fail as the component is nowhere to be found in the DOM.
For a component to be ajax-updated, it must already be in the DOM in the browser, this is the way ajax works. So the solution is to wrap the content of the popup panel in a component that will always be rendered.
That aside, even if you got the rendering correct, your popup will only be placed in the DOM. You actually need to call show() on the popup to get it to showup
To achieve what you want however, a better alternative will be to
Conditionally display the popup using javascript. If the condition is met, the show() function is called for the popup. Otherwise, an empty, do-nothing javascript function will be called.
<a4j:commandButton value="Compute Mission" action="#missionHandler.generateMissionFeasability}"
oncomplete="#{not empty missionHandler.exceptions ? #{rich:component('popupPanel')}.show()" : doNothing()}" render="popupPanel">
</a4j:commandButton>
For the doNothing js:
<script> function doNothing(){} <script/>
Take the rendering condition out of the modal panel
EDIT: Additionally, the show attribute on the popup component can be based on the same EL condition as the oncomplete attribute

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

Resources