Multiple rowSelection listeners in p:dataTable - java

I need advise with selection listeners in p:dataTable. My view is primefaces extension layout
(pe:layout), with north, left and center section. North section is table and left is a menu.
The idea is that left menu provides a different view for the data in selected table row
and therefore, if the row changes, the whole view need to refresh. (Depending on the view selected in left menu - these are backed by beans backigOne, two and three and only one at the time is active).
So I decided to use multiple rowSelect events for the table, for every bean:
<p:dataTable id="" var="wo" value="#{TableBacking.dataList}"
selection="#{TableBacking.selectedItem}" selectionMode="single" rowKey="#{wo}">
<p:ajax event="rowSelect" listener="#{backingOne.tableChangeListnener}"
update="..." disabled="#{leftMenuBacking.selection != "ONE" />
<p:ajax event="rowSelect" listener="#{backingTwo.tableChangeListnener}"
update="..." disabled="#{leftMenuBacking.selection != "TWO"/>
<p:ajax event="rowSelect" listener="#{backingThree.tableChangeListnener}"
update="..." disabled="#{leftMenuBacking.selection != "THREE" />
...
</p:dataTable>
This works well and since always one item is selected in leftMenuBacking, only one ajax event will be enabled at the time.
The problem is, however, all the backingOne, backingTwo and backingThree beans gets always initialized, because of the declaration:
listener="#{backingXXX.tableChangeListnener}"
and in my case, initialization is costly :( (timewise)
I need a hint of better desing, so the functionality is the same, but only the backing bean of the menu which is curently selected (or enabled as in sense of disabled="#{leftMenuBacking.selection != "ONE") gets initialized and no other does?
Thanks a lot.
EDIT 1 based on Magnus Tengdahl comment:
There are different pages for every selection in leftmenu.
The page backingOne.xhtml looks something like this:
<ui:composition template="/publicResources/templates/gdfoxTemplate.xhtml">
<ui:define name="title">GDFox | Similar Problems</ui:define>
<!-- North contains the section with table that produces rowSelect event as described above -->
<ui:define name="north">
<ui:include src="currentWorkload.xhtml" />
</ui:define>
<!-- Contains the left munu, uses p:menu widget-->
<ui:define name="west">
<ui:include src="leftmenu.xhtml" />
</ui:define>
<!-- center view based on BackingOne bean-->
<ui:define name="center">
....
</ui:define>
</ui:composition>
So basically, every selection on left menu is on different URL (let's just call them backinOne/Two/Three.xhtml. They share all share north and west sections and defines unique center
This is why needed to use three for that table - in order to propage the change to all possible views (althought only one will be selected in time). I dont thing (== I am still failing to see) Magnus that one listenere would solve my problem. If you are sure your idea will work, could you please explain it little bit further?

I would go with having just one rowSelect listener, without any disabled conditions:
<p:ajax event="rowSelect" listener="#{backingOne.tableChangeListnener}"
update="menuArea" />
Then let the backing bean decide what should be done given the selected row. Have a look at the PrimeFaces ShowCase for an example on how to get the selected row in the backing bean. From that information the backing bean surely must know what to do. Basically, you could just call the appropriate backing bean implementation in one of the backing beans.
You haven't given us any code for the left view, but I would recommend something like (if you don't do already):
<h:panelGroup id="menuArea">
<ui:fragment rendered="#{leftMenuBacking.selection == 'ONE'}">
... code for selection "ONE" ...
</ui:fragment>
<ui:fragment rendered="#{leftMenuBacking.selection == 'TWO'}">
... code for selection "TWO" ...
</ui:fragment>
<ui:fragment rendered="#{leftMenuBacking.selection == 'THREE'}">
... code for selection "THREE" ...
</ui:fragment>
</h:panelGroup>
In this way, your left area will be re-rendered (i.e. updated) for every rowSelect and your selection table is left as it is.

I think you could make an extra bean backingbean0. In that bean you have the listener tableChangeListener. As Magnus stated you have 1 p:ajax
And in the tableChangeListener you can call backingbean1 2 or 3 depending on the one that is needed. (you have this info in your backingbean0)
protected Object getBean(String name) {
FacesContext fcontext = FacesContext.getCurrentInstance();
ELResolver resolver = fcontext.getApplication().getELResolver();
Object bean = resolver.getValue(fcontext.getELContext(), null, name);
if (bean == null) {
getLog().warn("An object with name=" + name + " could not be resolved with ELResolver");
}
return bean;
}
and you call it like this
BackingBean1 bb1 = (BackingBean1) getBean("bb1");

Related

rendering panelgrids on tab change

I have two grids, and one tabView which contains 2 tabs, for first tab I have to show panelgrid1, and for tab2 = panelgrid2. I have used rendered attribute for both panels, and used tabchange event in tabView, this listener updates the status attribute in java, but in xhtml, same grid is still shown and doesn't change 2nd panelgrid.
You need to make absolutely sure that you refer in ajax update/render a component which is always rendered. It is not possible to refer a component which is by itself conditionally rendered in order to show/hide it.
<p:ajax ... update="foo" />
...
<h:panelGroup id="foo">
<h:panelGrid ... rendered="#{bean.grid == 1}">
...
</h:panelGrid>
<h:panelGrid ... rendered="#{bean.grid == 2}">
...
</h:panelGrid>
</h:panelGroup>
See also:
Why do I need to nest a component with rendered="#{some}" in another component when I want to ajax-update it?

ui:include dependent on viewParam

I have a page where I want to include a part of the page (footer in this instance) dependant on values given from a view parameter.
I have my ViewScoped backing bean initializing on preRenderView
<f:metadata>
<f:viewParam name="racecode" value="#{displayResults.racecode}" />
<f:event type="preRenderView" listener="#{displayResults.init}" />
</f:metadata>
This queries the database to get the name of the footer to be included. This then, is used in this fashion :
<h:panelGroup id="customFooter" display="block">
<ui:include src="#{displayResults.customFooter}" />
</h:panelGroup>
This always gives me a missing page. But if I enter the page name manually it works. Same if I replace the ui:include with an h:outputText.
I understand that it has something to do with the phases of JSF and that at the time the ui:include is done, the value is not set yet. (reading up and better understanding the phases is something on my TODO list).
The question remains. How can I get something of the sort done. Have a bean use the viewParam, query the database and use that value in a ui:include?
#wemu has already explained the cause. The <ui:include src> is evaluated before init() method is called. His proposed <f:phaseListener> solution is however clumsy.
Just use #ManagedProperty/#PostConstruct on a #RequestScoped bean.
#ManagedProperty("#{param.racecode}")
private String racecode;
#PostConstruct
public void init() {
// ...
}
PreRenderView listeners are called within the RenderResponsePhase, before components are rendered BUT AFTER the TagHandlers are called. This means that TagHandlers will NOT see data initialized within a PreRenderView event.
If you are using a <ui:include value="#{myBean.myViewId}" /> to dynamically switch an include you can't use a PreRenderView event listener to set the myViewId property of myBean.
If you need to do that use a <f:phaseListener>.

How can I reset JSF UIInput components to their managed bean values

I want to reset JSF inputs to their original managed bean values after validation failed.
I have two forms inside the same page - the first form has a commandLink to initialize the second form. The second form is rendered as a dialog whose visibility is toggled through jQuery - for the purpose of this exercise, though, I can illustrate just with two forms on the same page. Also, while I'm using PrimeFaces 2.2.x in my app, the same behaviors appear with regular h:commandLink as well.
The issue I'm having is:
click link in first form to initialize second form
submit invalid values in second form
click link in first form again to initialize second form - invalid values still there and/or UIInput state is still invalid.
For example - take the following form
<h:form id="pageForm">
<h:commandLink actionListener="#{testBean.initialize}">Initialize, no execute
<f:ajax render=":dialogForm"/>
</h:commandLink>
<br/>
<h:commandLink actionListener="#{testBean.initialize}">Initialize, execute=#this
<f:ajax execute="#this" render=":dialogForm"/>
</h:commandLink>
</h:form>
<h:form id="dialogForm">
<h:messages/>
String property - Valid: <h:outputText value="#{property.valid}"/>
<br/>
<h:inputText id="property" binding="#{property}" value="#{testBean.property}">
<f:validateLength minimum="3"/>
</h:inputText>
<br />
Int property - Valid: <h:outputText value="#{intValue.valid}"/>
<h:inputText id="intValue" binding="#{intValue}" value="#{testBean.intValue}">
<f:validateLongRange maximum="50" />
</h:inputText>
<br/>
<h:commandLink actionListener="#{testBean.submit}">
Submit
<f:ajax render="#form" execute="#form"/>
</h:commandLink>
<h:commandLink actionListener="#{testBean.initialize}">Initialize, execute=#this
<f:ajax execute="#this" render="#form"/>
</h:commandLink>
</h:form>
Bean class:
#ManagedBean
#ViewScoped
public class TestBean {
private String property = "init";
private Integer intValue = 33;
// plus getters/setters
public void submit() { ... }
public void initialize() {
intValue = 33;
property = "init";
}
}
Behavior #1
click either "Initialize" link on the pageForm
inputs get initialized to "init", "33"
now submit something invalid for both fields like "aa", "99"
now click any of the "initialize" links again (they all seem to behave the same - makes no difference whether it's in the same form or different, or whether I have specified execute="#this" or not.)
Result => UIInput.isValid() = false, both values reset though ("init", "33").
Expected => valid = true (or is this not reasonable to expect?)
Behavior #2
click either "Initialize" link on the pageForm
inputs get initialized to "init", "33"
now submit something invalid for the text field but valid for the int field ("aa", "44")
now click any of the "initialize" links again
Result => "init", valid=false; 44, valid=true
Expected => "init", valid=true; 33, valid=true
I have also looked at:
JSF 2 - Bean Validation: validation failed -> empty values are replaced with last valid values from managed bean
and
How can I populate a text field using PrimeFaces AJAX after validation errors occur?
The suggestion to explicitly reset the state of UIInputs with resetValue does work, but I'm not happy with it.
Now, I sort of understand why the isValid is not resetting - my understanding of the JSF lifecycle is that once a value is submitted to a component, isValid is not reset until the component is successfully submitted and validated and the Update Model Values phase sets the bean value. So there may be no way around explicitly resetting the valid state in this case, since I want to use #{foo.valid} for conditional CSS styling.
What I don't understand, though, is why the components that successfully validated are not re-initializing from the bean. Perhaps my understanding of the JSF lifecycle is slightly off?
I understand the rules layed out in the answer to How can I populate a text field using PrimeFaces AJAX after validation errors occur? as they pertain to an individual component but not to the form as a whole - i.e., what happens if a component succeeds validation but the validation overall fails?
In fact, there may turn out to be no better way than explicitly calling resetValue on components. In my case, all of the dialogs are in the same big JSF view tree with the underlying page that opens them. So from JSF's perspective, the same view component state including invalid input values should be preserved until we navigate away from the view, as it has no visibility into how we're toggling display attributes client-side.
The only other thing that might work is if the components that make up the dialog are actually not rendered in the JSF view tree unless they're visible. In my case, they're always rendered, using CSS to toggle visibility.

Conditionally rendered button, design, calling an action

JFS1.2 + Richfaces 3.3
Situation is as follows:
JSP page renders conditionally one or another panelGroup.
Within each panelGroup there are couple setters and one command button.
Each of two panelGroups uses own bean for setting and performing action.
On the top of a page there's selectOneRadio with (obvious) two items - coresponding tow options of conditional rendering.
Page renders properly, switcher causes to render appropriate panel.
Case is, commands buttons doesn't call an action.
I know what's going on - when I click a button to call action dom is regenerated, but the value that hold my decision to display particular panel doesn't exist anymore. The button is not recreated, action is not fired.
Technically:
<h:selectOneRadio value="#{reportType}">
<f:selectItem itemLabel="x" itemValue="x"/>
<f:selectItem itemLabel="y" itemValue="y"/>
<a4j:support event="onclick" reRender="xPanel, yPanel/>
</h:selectOneRadio>
<h:panelGrid id="xPanel "columns="2" rendered="#{reportType eq 'x'}">
<...some setters>
<... commandbutton>
</h:panelGrid>
<h:panelGrid id="yPanel "columns="2" rendered="#{reportType eq 'y'}">
<...some setters>
<... commandbutton>
</h:panelGrid>
Question is, how to design the page to obtain proper rendering and actions?
For now, I created additional session bean that holds switching value (x|y), but that desing smells bad for me...
RichFaces 3.3 offers the <a4j:keepAlive> tag which does basically the same as Tomahawk's <t:saveState> and JSF2 #ViewScoped. Add the following line somewhere in your view:
<a4j:keepAlive beanName="#{bean}" />
This will keep the bean alive as long as you're returning null or void from action(listener) methods.
See also:
JSF 1.2: How to keep request scoped managed bean alive across postbacks on same view?

How to specify the order in which a rich:extendedDataTable selection and h:selectOneMenu value are applied

I'm working on an application that's basically a customised database administration tool.
The page structure is basically the following:
<a4j:region>
<h:selectOneMenu value='#{bean.selectedTable}'>
...
<a4j:ajax event='change' render='tablePanel'/>
</h:selectOneMenu>
<a4j:outputPanel id='tablePanel'>
<rich:extendedDataTable id='table' selection='#{bean.selectedRows}' ...>
<f:facet name='header'>
[datascroller etc.]
<a4j:commandButton action='#{bean.deleteSelectedRows}' execute='#region' render='tablePanel'/>
</f:facet>
[columns]
</rich:extendedDataTable>
</a4j:outputPanel>
<a4j:region>
The selectOneMenu is used to choose which database table will be displayed. The backing bean is request scoped and set up to pick the first available table as a default when it's initialised. I'm using an ExtendedDataTable subclass to paginate data in the database.
When I click the commandButton to delete the rows, it seems that the extendedDataTable component determines the selected rows /before/ the value of bean.selectedTable is applied. This means that no matter what table is selected in the dropdown menu, RichFaces tells me the selected rows are some (more or less arbitrary) rows in the default database table.
I verified that this is an ordering problem, when deleteSelectedRows() is called the value of selectedTable is correct. I'm using Richfaces 4 M6, and a4j:keepAlive doesn't seem to be there anymore to preserve the bean state.
Is there a way to tell RichFaces / JSF in which order to do these things? I tried using immediate="true" on the h:selectOneMenu but that didn't help.
Also, after a delete, the tablePanel doesn't seem to be rerendered, while another a4j:commandButton that adds new records with the same execute and render attributes seems to work fine. Is there a way to debug the state of RichFaces ajax requests / hook into them via events?

Categories