How to collapse or expand all RichFaces collapsiblePanel elements on the page? - jsf

I'm using RichFaces with JSF to develop a simple app. One page of this app contains several collapsiblePanel elements. Some of the collapsiblePanel elements are nested, but never more than a second layer.
I would like to provide links or buttons on the page to expand all and collapse all collapsiblePanel elements on the page. How can I do that?
The elements currently use the switchType="client" attribute to let the client handle the expanding and collapsing. I suspect that using a type of ajax instead may help, but I'm not sure nor do I know how I would take advantage of it.
Update: My question may be easier to understand if I include an example of what I'm trying to do:
<h:form>
<a4j:commandButton actionListener="#{bean.setDefaultExpanded(true)}"
render="reportPanel" value="Expand all" />
<a4j:commandButton actionListener="#{bean.setDefaultExpanded(false)}"
render="reportPanel" value="Collapse all" />
<h:panelGrid id="reportPanel">
<ui:repeat var="account" value="#{bean.results.entrySet().toArray()}">
<rich:collapsiblePanel expanded="#{bean.defaultExpanded}">
<ui:repeat var="chargeGroup" value="#{account.value.entrySet().toArray()}">
<rich:collapsiblePanel expanded="#{bean.defaultExpanded}">
<h:outputText value="content: #{chargeGroup.value}" />
</rich:collapsiblePanel>
</ui:repeat>
</rich:collapsiblePanel>
</ui:repeat>
</h:panelGrid>
</h:form>

The <rich:collapsiblePanel> has the expanded attribute, you can bind it to bean property and control the expansion from there. Something like this
<rich:collapsiblePanel id="panel1" expanded="#{bean.expanded}" …>
<a4j:commandButton actionListener="#{bean.togglePanels()}"
… render="panel1, panel2, …"/>
The switchType controls where the content is pulled from, not how you expand/collapse the panel.

I had the same problem when click individual panels and then expand/collapse all. This work for me (richfaces 4.2.3):
<h:commandButton immediate="true" action="#{controllerBean.toggleMin}" value="collapse all" >
<a4j:ajax render="panel1 panel2"></a4j:ajax>
</h:commandButton>
<h:commandButton immediate="true" action="#{controllerBean.toggleMax}" value="expand all">
<a4j:ajax render="panel1 panel2"></a4j:ajax>
</h:commandButton>
...
<rich:collapsiblePanel id="panel1" immediate="true" expanded="#{modelBean.expanded}" header="Title text" switchType="client">
...
</rich:collapsiblePanel>
...

Related

<h:commandButton> is not refreshing page after actioned

hi we are using along with a4j tag.
here we are retrieving data from database after a click of button. even though the data is available in server, it will not display over view. After manual refresh of web page will lead to data diplay.
here is code snippet
.... some code here
<rich:tab id="menu5" label="Recall">
<ui:include src="/pages/mctrans/reCallMcifTrans.xhtml" />
</rich:tab>
reCallMcifTrans.xhtml contains below code
<h:commandButton type="button" id="reCallbutton1" value=" Search "
styleClass="commandExButton">
<a4j:support event="onclick" id="ajsf12"
oncomplete="javascript:alert('Search Completed');javascript:document.body.style.cursor='default';"
action="#{mcifRecallTransBean.reCallSearch}" reRender="reCallgrid1" />
</h:commandButton>
It looks like you're working with RichFaces 3.3. So, you don't need a <h:commandButton with <a4j:support> because you can use <a4j:commandButton> that already does this. You can refactor your code to this:
<a4j:commandButton type="button" id="reCallbutton1" value="Search"
styleClass="commandExButton"
action="#{mcifRecallTransBean.reCallSearch}"
reRender="reCallgrid1"
oncomplete="javascript:alert('Search Completed');javascript:document.body.style.cursor='default';" />
Make sure your reCallgrid1 component is available in the same <h:form> of the <a4j:commandButton>.
Since you also want to add a Wait while searching the data behavior when the button is clicked, you can use <a4j:status> along with the <a4j:commandButton> as shown in the <a4j:status> demo. Here's a basic example:
<a4j:commandButton type="button" id="reCallbutton1" value="Search"
styleClass="commandExButton"
action="#{mcifRecallTransBean.reCallSearch}"
reRender="reCallgrid1" />
<!-- Note that there's no oncomplete in this case -->
<a4j:status for="reCallbutton1">
<f:facet name="start">
<h:graphicImage value="/res/images/wait.gif"/>
</f:facet>
</a4j:status>
At last but not least, you should switch your managed bean to request scope and use RichFaces powerful <a4j:keepAlive> in order to simulate JSF 2 #ViewScoped. You can even use it in form of annotation on your managed bean (no additional configuration):
#KeepAlive
public class McifRecallTransBean {
//managed bean code here...
}
When you are using request parameters inside the bean, you need to pass them again with your action :
<h:commandButton type="button" id="reCallbutton1" value="Search" styleClass="commandExButton">
<a4j:support event="onclick" id="ajsf12" oncomplete="javascript:alert('Search Completed');javascript:document.body.style.cursor='default';" action="#{mcifRecallTransBean.reCallSearch}" reRender="reCallgrid1" />
<f:param name="param1" value="#{param['param1']}" />
<f:param name="param2" value="#{param['param2']}" />
</h:commandButton>

Custom selectItems

i want to customize selectItems
to display an image conditionally beside each checkbox
so first i tried to display the image for all checkboxes
but it gets displayed only once, here's what i tried:
<h:selectManyCheckbox value="#{myBean.checkboxesArry}" layout="pageDirection">
<f:selectItems value="#{myBean.mapOfCheckBoxes}" var="entry">
<label>
<ice:graphicImage url="/resources/images/myImage.bmp"/>
<b>#{entry.value}</b>
</label>
</f:selectItems>
</h:selectManyCheckbox>
please advise how to accomplish that ?
You cannot nest UI compnents in <f:selectItems> that way. I however see that you're using ICEfaces, you should then be able to use <ice:selectManyCheckbox layout="spread"> in combination with <ice:checkbox> instead.
<ice:selectManyCheckbox id="foo" value="#{myBean.checkboxesArry}" layout="spread">
<f:selectItems value="#{myBean.mapOfCheckBoxes}" />
</ice:selectManyCheckbox>
<c:forEach items="#{myBean.mapOfCheckBoxes}" var="entry" varStatus="loop">
<ice:checkbox for="foo" index="#{loop.index}" />
<ice:graphicImage url="/resources/images/myImage.bmp" />
<b>#{entry.value}</b>
</c:forEach>
(untested as I don't use ICEfaces, but the above construct works for Tomahawk, from which ICEfaces has basically copied the implementation; you can also use <ui:repeat> but it only supports Map since JSF 2.1)
See also:
Radio buttons in different parts of the page

form within form: skip validation of parent form

<h:form prependId="false" id="parentForm">
...
<h:form prependId="false" id="commentForm">
...
add comment
</h:form>
save
</h:form>
Doesn't work...
Without the inner form the parent's elements get validated when I just want to add a comment.
"add comment" should just validate the comment and when "save" is clicked the parent should be validated.
Nesting forms is illegal in HTML, so also in JSF since all it does is just generating HTML. You need to put them next to each other.
If you have multiple buttons in the same form of which you'd like to skip certain validation on certain button press, then add immediate="true" to the button in question. This way all input fields which do not have immediate="true" will be skipped.
See also:
What is the immediate attribute used for?
Update: OK, you want two physically separate forms inside a single form. If splitting the "God Form" in multiple forms with each its own responsibility is not an option, then there are several ways to go around this:
If you don't use Ajax and you just have a required="true" on an input element which you actually want to make non-required when you press a certain button, then do:
<h:form>
...
<h:commandButton value="Submit form but but do not validate comment" />
...
<h:inputTextarea id="comment" required="#{not empty param[foo.clientId]}" immediate="true" />
<h:commandButton binding="#{foo}" value="Submit and validate comment" immediate="true" />
</h:form>
If you actually use Ajax, then just specify the execute region in execute attribute.
<h:form>
<h:panelGroup id="other">
....
<h:commandButton value="Submit form but but do not validate comment">
<f:ajax execute="other" render="other" />
</h:commandButton>
</h:panelGroup>
<h:panelGroup id="comments">
<h:inputTextarea required="#{not empty param[foo.clientId]}" />
<h:commandButton value="Submit and validate comment by ajax">
<f:ajax execute="comments" render="comments" />
</h:commandButton>
</h:panelGroup>
</h:form>

How to do client side updates in JSF 2.0

I'd like to do client side component updates. Example: disable a button when a checkbox is clicked:
<h:selectBooleanCheckbox value="true" onchange="button.disabled=true" />
<h:commandButton id="button" value="Save" />
The above doesn't work. Is there a way to do this declaratively in JSF?
It doesn't work because the JSF component ID is not necessarily the same as the generated HTML ID. JSF composes the HTML element ID and form element name based on the ID's of all UINamingContainer components in the tree -if any. The UIForm component is one of them. Rightclick the page in webbrowser and choose View Source. See and learn.
There are two ways to solve this problem:
Set prependId attribtue of <h:form> to false.
<h:form prependId="false">
<h:selectBooleanCheckbox value="true" onclick="button.disabled=true" />
<h:commandButton id="button" value="Save" />
</h:form>
Note that I'd rather use onclick="button.disabled=!checked" instead. The onchange is only fired when the input element loses focus and that is non-intuitive in case of checkboxes and radiobuttons. Also, you'd like to turn it back on when the checkbox is checked again, right?
Give the <h:form> a fixed ID so that you know how the generated form element name look like:
<h:form id="form">
<h:selectBooleanCheckbox value="true" onclick="form['form:button'].disabled=true" />
<h:commandButton id="button" value="Save" />
</h:form>
The brace notation is mandatory because the : is an illegal identifier character and just onclick="form:button.disabled=true" won't work. Also here, I'd rather use onclick="form['form:button'].disabled=!checked instead.
The same problem applies on document.getElementById() as well. So simply replacing button by document.getElementById('button') alone won't fix the problem.
simply use javascript
document.getElementById("buttonId").disabled="true";
Check this

Absolute reRendering using RichFaces

My problem is that RichFaces reRender does not work 'under' the current element in the element tree; only upper elements get rerendered.
Is there any way to access lower elements with AJAX?
Any help would be appreciated!
Daniel
EDIT I edited this question to a more general one. See revisions if interested in the original question.
reRender works with providing an the id of the target object you want to reRender (inside the same naming container - form most often)
the id should be a unique string, according to html spec
reRender allows dynamic value - i.e. reRender="#{myBean.currentItemsToRerender}
Based on that I think you should be able to achieve what you want (although it's not entirely clear)
Update:
UIComponent.findComponent(..) has a well-defined algorithm for resolving ids. So for absolute referencing your reRendered id should start with : and then continue through the hierarchy of the naming containers.
Here is an example where changePanel111() changes the content of a lower element:
<h:form id="form" prependId="true">
<rich:panel id="PANEL1">
<h:outputText id="PANEL1TEXT" value="#{ajaxTestBean.panel1}"/>
<rich:panel id="PANEL11">
<h:outputText id="PANEL11TEXT" value="#{ajaxTestBean.panel11}"/>
<rich:panel id="PANEL111">
<h:outputText id="PANEL111TEXT" value="#{ajaxTestBean.panel111}"/>
</rich:panel>
</rich:panel>
<rich:panel id="PANEL12">
<h:outputText id="PANEL12TEXT" value="#{ajaxTestBean.panel12}"/>
<br/>
<a4j:commandLink value="CHANGE PANEL12" action="#{ajaxTestBean.changePanel12}">
<a4j:support reRender="PANEL12" event="onclick"/>
</a4j:commandLink>
<br/>
<a4j:commandLink value="CHANGE PANEL111" action="#{ajaxTestBean.changePanel111}">
<a4j:support reRender="form:PANEL111" event="onclick"/>
</a4j:commandLink>
</rich:panel>
</rich:panel>
</h:form>
Notice how the lower element needs to be identified as form:PANEL111.
Hope this helps!
reRender can point to any component outside the form as well. For example this works:
<h:form>
<a4j:commandButton reRender="panel"/>
</h:form>
<h:panelGrid id="panel">
...
</h:panelGrid>
For my MyFaces+Richfaces App, <rich:panel> tag was not working as described in the selected answer. When I changed it to <a4j:outputPanel ajaxRendered="true" />, it started working as given here "<a4j:commandLink> Not Rerendering"
Configuration: MyFaces 2.1.10(Facelets used for templating) and Richfaces 4.2.3.
Hope this will help.

Resources