Expanding behavior of Richfaces Collapsible Panel - jsf-2

So, I have recently started working on a JSF page using richfaces 4, in which I have a rich:collapsiblePanel. I face a problem however, I am using the collapsiblePanel within a rich:dataGrid, which renders the collapsiblePanels by iterating though a list recieved from the server. There is a link to 'sort' the collapsiblePanels according to the data in the panel header (in the backing bean of course). When any of the collapsiblePanels are expanded, and the sort link is clicked, all of them are expanded, whilst all are expanded, if one is closed, and the link clicked again, all of them close.
Things I have tried:
Changing the switchType to any other than client (i.e ajax and server)
Adding a constant boolean in the backing bean to force the expand attribute to false on reload (although it is not even affected by the backing bean at all)
Sample code of what it looks like at the moment:
<h:panelGrid id="SomePanelGrid">
<rich:dataGrid id="SomeDataGrid" value="bean.listValues" var="values"
iterationStatusVar="counter" elements="10">
<rich:collapsiblePanel switchType="client" expanded="#{bean.expanded}">
Layouts and what not (not in relation to this)
</rich:collapsiblePanel>
</rich:dataGrid>
</h:panelGrid>
The link simply calls a method in the backing bean which does the sorting.
I have found a similar problem, involving a dataTable instead of a dataGrid, although no answers have been given, but only links that lead to more dead ends. This can be found at: https://community.jboss.org/message/819938
Any help would be greatly appreciated. Unfortunately I do not have alot of time to answer alot of other questions at the moment, but I will be checking back a bit later.
Thanks in advance.

You have many syntax flaws inside your code, here is how it should looks like :
<h:panelGrid id="SomePanelGrid">
<rich:dataGrid id="SomeDataGrid" value="#{bean.listValues}" var="values"
iterationStatusVar="counter" elements="10">
<rich:collapsiblePanel switchType="client" expanded="#{bean.expanded}">
Layouts and what not (not in relation to this)
</rich:collapsiblePanel>
</rich:dataGrid>
</h:panelGrid>
You are probably experiencing only one collapsiblePanel in your example, this code modified and tested work properly.
Now if you want to save collapsiblePanels expanded state when refreshing your dataGrid by AJAX, you need to add some stuff.
First, you need to add one property to your objects you are iterating on, to save the state of each panels.
public class Item
{
private boolean expanded;
public void setExpanded(boolean expanded)
{
this.exanded = expanded;
}
public boolean getExpanded()
{
return this.expanded;
}
// Your other stuff
}
Second, you need to add a listener in your bean to know when user changes the state of a panel, note the attribute to get back which item is related to this panel.
#ManagedBean
#ViewScope
public class Bean
{
private List<Item> listValues;
#PostConstruct
void init()
{
listValues = //... Some initialization to your list
}
public List<Item> getListValues()
{
return this.listValues;
}
public void toggle(PanelToggleEvent event)
{
// Take the current item
Item item = (Item)event.getComponent().getAttributes().get("item");
// Save the current state in the item
item.setExpanded(event.getExpanded());
}
}
Finally, you need to change your switchType to AJAX and add the listener in your code without forgetting the attribute that need to be passed in the listener.
<h:form>
<rich:dataGrid id="SomeDataGrid" value="#{bean.listValues}" var="item"
iterationStatusVar="counter" elements="10">
<rich:collapsiblePanel switchType="ajax" expanded="#{item.expanded}">
<f:attribute name="item" value="#{item}" />
Layouts and what not (not in relation to this)
</rich:collapsiblePanel>
</rich:dataGrid>
</h:form>

Related

How to submit a form with disabled input that got a conversion error

I have the following page/bean structure (using myfaces 2.0.11 + tomcat 6)
I have one checkbox that when checked the h:inputtext (which is connected to an Integer variable in my bean) next to him is enabled and when the checkbox is unchecked the input is disabled, and I have a submit button that submits them both (entire form)
here is the code
<h:form prependId="false">
<h:selectBooleanCheckbox id="my_len" value="#{myBean.myLenBool}">
<f:ajax render="my_len_input_wrapper"/>
</h:selectBooleanCheckbox>
<h:panelGroup id="my_len_input_wrapper">
<h:inputText value="#{myBean.myLen}" id="my_len_input"
disabled="#{not myBean.myLenBool}" required="#{myBean.myLenBool}">
<f:validateLongRange minimum="1"/>
</h:inputText>
<h:message for="my_len_input"/>
</h:panelGroup>
<h:commandButton action="#{myBean.submit}" value="submit">
<f:ajax render="#form" execute="#form"></f:ajax>
</h:commandButton>
</h:form>
Bean code
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
#ManagedBean
#SessionScoped
public class myBean {
Integer myLen;
boolean myLenBool;
public Integer getMyLen() {
return myLen;
}
public void setMyLen(Integer myLen) {
this.myLen = myLen;
}
public boolean isMyLenBool() {
return myLenBool;
}
public void setMyLenBool(boolean myLenBool) {
this.myLenBool = myLenBool;
}
public void submit() {
// submit
}
}
The scenario is as following
1) check the checkbox (the input will be enabled)
2) enter an invalid value (for example 0.5) , its invalid cause myLen is a Integer
3) hit submit -> a error message will be shown in h:message cause of conversion error
4) uncheck the checkbox (it will disable the inputtext)
5) hit submit <--- form is not being submitted cause of the conversion error?!?!
So the question is: how can I submit the form with disabled input that got conversion error???
The only solution I found so far is writing my own custom converter that ignores conversion if the field is disabled
#FacesConverter("APCustomConverter")
public class APCustomConverter extends IntegerConverter{
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
if (component.getAttributes().get("disabled") != null && component.getAttributes().get("disabled").equals(true)) {
return null;
}
Object retValue = super.getAsObject(context, component, value);
return retValue;
}
}
Was hoping for a better solution than mine (using CustomConverter),
A bit off/not so off topic:
This conversion error eventually had lead me to a really annoying scenario in which: when I used discard button with only render="#form" the state of the checkbox and the input went so wrong that after clicking discard -> the checkbox remained checked (although it shouldn't cause form wasn't really submitted) and the input was disabled and not readonly cause the real checkbox value was false, AND when i hit submit again the checkbox became really checked but the input got himself a null value (all this cause null pointers exception on server), so eventually I had to use <f:actionListener type="org.omnifaces.eventlistener.ResetInputAjaxActionListener" /> by omnifaces in the discard button.
Tested with Mojarra 2.1.26 and MyFaces 2.0.11. When unchecking the checkbox, Mojarra updates the h:inputText and leaves it blank, which is the expected behaviour because the value has never reached the model (validation error). However, MyFaces only updates it to disable mode, leaving the old input value there.
That seems like a MyFaces issue, not solved even in the latest (2.1.12-2.0.18) branch versions. In fact, if you want to skip some element's conversion/validation depending on its state, writing a custom converter/validator is the way to go, but in your case the issue is tied to MyFaces' ajax cycle, which should work as Mojarra's one does.
As a solution you have three possible choices:
Switch to Mojarra implementation if possible, that would make the issue disappear.
Write some JS code in order to reset the input value when the checkbox is unselected.
Go with your current solution, which skips element conversion at server side in case of being disabled.

RichFaces rich:autocomplete not firing valueChangeListener

Based on this Im implementing a rich:autocomplete with a dynamic list to the autocomplete. What i want to do next is, when the user edits this field to update another field based on its value.
I've found various links and references declaring this is a bug, that it triggers when the form in used is submitted, workaraounds but from at least a year ago. here, here and here.
Fragment of my code:
<rich:autocomplete value="#{cotizacionBean.currentOpcionEditable.sumaAsegurada}" style="width:100px;" id="sumaAseguradora"
required="true" validatorMessage="#{msgs['global.etiqueta.sumaAseguradoraRequerida']}"
autocompleteMethod="#{cotizacionBean.getAutocompleteListSumaAsegurada}"
valueChangeListener="#{cotizacionBean.sumaAseguradaModificada}"
requiredMessage="#{msgs['global.etiqueta.sumaAseguradoraRequerida']}" converterMessage="#{msgs['global.etiqueta.sumaAseguradoraRequerida']}">
</rich:autocomplete>
And my bean:
public void sumaAseguradaModificada(){
System.out.println("entro sumaAseguradaModificada");
}
So my questions are two: is this already fix? How can i do it?
Here's how I got that to work:
<a4j:jsFunction name="submitSearch"
execute="sumaAseguradora"
render="sumaAseguradora"
action="#{cotizacionBean.sumaAseguradaModificada}" />
<rich:autocomplete style="width:100px;" id="sumaAseguradora"
required="true" validatorMessage="#{msgs['global.etiqueta.sumaAseguradoraRequerida']}"
autocompleteMethod="#{cotizacionBean.getAutocompleteListSumaAsegurada}"
requiredMessage="#{msgs['global.etiqueta.sumaAseguradoraRequerida']}" converterMessage="#{msgs['global.etiqueta.sumaAseguradoraRequerida']}"
onselectitem="submitSearch()"
value="#{cotizacionBean.currentOpcionEditable.sumaAsegurada}">
</rich:autocomplete>
In the Bean, I had to change the signature of the method:
public Object sumaAseguradaModificada() {
System.out.println("entro sumaAseguradaModificada");
return "success";
}

Ajax attached to Composite Component updates model but does not fire listener

I have a custom component location. I want that, when a change is done, the model is update so another component (an autocomplete) is to show only results related to the location value. Also, that component is rerendered to reset it.
I have a page with the following code (simplified):
<h:form id="ticketForm">
...
<loc:location id="locationId" <-- CUSTOM COMPONENT
hiddenFieldValue="#{ticketCtrl.ticket.location}"
visibleFieldValue="#{ticketCtrl.ticket.locationDescription}"
rendered="#{ticketCtrl.ticketModifiable}">
<f:ajax event="changeLocation" render=":ticketForm:gfhId" <-- AJAX CALL.
execute=":ticketForm:locationId" listener="#{ticketCtrl.locationListener}"/>
</loc:location>
...
</h:form>
When the value in the component is changed, the model is updated and :ticketForm:gfhId is rendered as needed, but the listener(which performs additional resets) is not executed.
Attaching the ajax to a simpler control results in the listener being executed; v.g.
<h:inputText id="contactId"
value="#{ticketCtrl.ticket.contactPerson}"
disabled="#{not ticketCtrl.ticketModifiable}">
<f:ajax event="change" render=":ticketForm:gfhId"
execute=":ticketForm:locationId" listener="#{ticketCtrl.locationListener}"/>
</h:inputText>
works perfectly.
I do not know if it may be related as how the changeLocation event is fired; inside my component I define it as
<composite:interface>
...
<composite:clientBehavior name="changeLocation" event="change" targets="visibleId"/>
</composite:interface>
with visibleId being a readonly text; when it is changed by javascript I fire the change event on it with JS.
function launchEvent(fieldName) {
if ("fireEvent" in fieldName) {
fieldName.fireEvent("onchange");
} else {
var evt = document.createEvent("HTMLEvents");
evt.initEvent("change", false, true);
fieldName.dispatchEvent(evt);
}
}
The info I find in other questions is about making ajax an inner part of the composite component, here I want ajax detached because I probably won't need it in other uses of the component.
I am using JBoss 6.1 wih Mojarra 2.1.9 and Richfaces 4.
Thanks in advance.
UPDATE:
Even after finding jarek.jpa's answer right, if someone wants to check the code it is here:
The composite component
http://pastebin.com/9wqMVfR5
The main form
http://pastebin.com/i39ys2D9
This may have to do with the "readonly" attribute set to true. Though you manage to send the change-event by hand (JS), the server-side processing of the listener may be dropped due to the readonly state. See e.g. jsf (richfaces) readonly input text validation.

DataScroller not rendererd after binding with bean

My goal is to reset the first page of the datascroller after a new search.
This should be done with this statement:
getTableScroller().getUIData().setFirst(index);
My problem is, after I bind the datascroller with a bean it will not be rendered. Just after I hit the search button a seccond time it will be rendered.
Bean:
public class HistoryBean {
private HtmlDataScroller tableScroller = new HtmlDataScroller();
// ...
Facelet:
<t:dataScroller id="scroll_1"
for="data"
fastStep="10"
pageCountVar="pageCount"
pageIndexVar="pageIndex"
styleClass="scroller"
paginator="true"
paginatorMaxPages="9"
paginatorTableClass="paginator"
paginatorActiveColumnStyle="font-weight:bold;"
immediate="true"
actionListener="#{historyBean.scrollerAction}"
binding="#{historyBean.tableScroller}"
>
If I remove the binding attribute it will be rendered on initial request. What have I forgotten?
You shouldn't bind the component to a bean which is in a broader scope than the request scope.
Rather specify the first attribute just straight in the view.
<t:dataScroller first="#{historyBean.index}" ...>

How to display a confirmation dialog(Primefaces) from backing bean

I have an import function which will parse the XML file which contains the version information of the document and save it in database. If user try to upload the already existing version, I need to show the confirmation dialog like " Version already exists do you wants to overwrite..?" ok, Cancel.
I am using Mozarra 2.0.3, Prime faces 2.2 RC2, Glass Fish 3 and I am trying this way.
<h:form id="conDialog">
<p:commandButton value="getConfirmMsg" update="conDialog" action="#{buttonBean.getConfirmMsg()}"
oncomplete="confirmation.show()"/>
<p:growl id="messages1" globalOnly="true"/>
<p:confirmDialog message="Version already exists. Do you want to override it?"
rendered="#{buttonBean.showConfirm}"
header="Version already exist" severity="alert" widgetVar="confirmation">
<p:commandButton value="OK" update="messages1" oncomplete="confirmation.hide()"
action="#{buttonBean.overrideVersion}" />
<p:commandButton value="Cancel" onclick="confirmation.hide()" type="button" />
</p:confirmDialog>
</h:form>
BackingBean
#ManagedBean
#RequestScoped
public class ButtonBean {
boolean showConfirm = false;
public boolean isShowConfirm() {
return showConfirm;
}
public void setShowConfirm(boolean showConfirm) {
this.showConfirm = showConfirm;
}
public void overrideVersion() {
System.out.println("Version alrady exists...Overriding...");
FacesMessage msg = new FacesMessage("Action is successful");
FacesContext.getCurrentInstance().addMessage(null, msg);
}
public void getConfirmMsg() {
System.out.println("Inside getConfirmMsg()....");
showConfirm = true;
System.out.println("showConfirm: " + showConfirm);
}
}
When I click on "OK" the action is not firing. Is there any mistake in the above code?
It's not possible to get confirmation from the client during processing on server.
You have two options:
Get overwrite permission before calling your action method e.g. with a checkbox "Overwrite file if exists?" or
You have to stop processing, set a flag and return null to reload current page in browser.
Then you could display the p:dialog depending on flag status.
You are facing in typical Primefaces Problem.
When your page is displayed and buttonBean.showConfirm = false, this element is not rendered. This means, it will not appear in the DOM-Tree. No matter what you do afterwards, a non existing element cannot be shown or hidden.
There are actually two ways to solve you problem.
Use a remote command, so that the not rendered HTML-Code will be transmitted from you server.
Use css "display: none" instead of rendered="false".
I faced very similar question. The solution I came up with was to split the logic into 2 - first, when the button is pressed use 'action' to prepare the data for the validation and use 'oncomplete' to run a remote command which displays the confirmation dialog in which 'OK' is the real action.
Case
Validate that there is a selected item by pressing the Delete button. If you open a popup, if not shown in the error message, growl.
Alternative:
(Sorry I had trouble posting the code)
XHTML:
View xhtml
TestManageBean:
Manage bean
MORE INFO: www.primefaces.org/showcase/ui/dialogLogin.jsf

Resources