Primefaces autoUpdate only works once - jsf

I have a page with different calculations, which are done by ajax.
At the end of the page there should be a hint text, which is updated after each calculation.
For this I use the autoUpdate feature of Primefaces.
When I load the page initially the text is displayed correctly. Also after the first calculation the text is refreshed correctly.
But if I do further calculations, the text will not be changed anymore, no matter which value balanceController.getBalance() returns.
When I debug my code I see that balanceController.getDetails() runs correctly and also returns the desired text. Only the content on my page is not refreshed. When I manually reload the page (with the browser) the correct text appears.
What could be the cause that <p:autoUpdate/> is only executed during the first calculation and updates the tab content?
balancePage.xhtml
<p:tab title="Further details" rendered="#{balanceController.showDetails()}">
<p:autoUpdate/>
<h:outputText value="#{balanceController.details}"/>
</p:tab>
BalanceController.java
public String getDetails() {
if ( getBalance() >= 0 ) {
return "Your current balance is: " + Double.toString(getBalance());
} else {
return "Your credit has been used up!";
}
}

In general a p:tab is not updateable, as the one who renders the p:tab is the parent component. Probably a p:accordionPanel or p:tabView.
So you can either move the p:autoUpdate to the parent component or move it inside the h:outputText.
NOTE: you probably need to add a id to the h:outputText as only components with rendered id are updatetable and h:outputText skips rendering the id when no id attribute is explicitly set.
Its also possible to solve it by wrapping it a p:outputPanel:
<p:tab title="Further details" rendered="#{balanceController.showDetails()}">
<p:outputPanel>
<p:autoUpdate/>
#{balanceController.details}
</p:outputPanel>
</p:tab>
I removed the h:outputText by performance reasons. IMO one should not use h:outputText for simple text (escape is not set to false) as this creates a UICompoment on the server side, which is not required.

Related

Can I have both both radio selection and row selection in a p:dataTable

I have a p:dataTable which needs to have both radio selection and row selection.
In primefaces.org/showcase we have checkbox and row selection but with multiple selection and only row selection and only radiobutton selection.
Below is my code
<p:dataTable value="#{streetListBean.streetList}" var="street"
selection="#{streetListBean.selectedStreet}"
rowKey="#{street.streetId}">
<p:column selectionMode="single" style="text-align:center" width="5%"/>
<p:column headerText="#{msgs['label.streetName']}">
<p:outputLabel value="#{street.streetName}"/>
</p:column>
<p:column headerText="#{msgs['label.postalCode']}">
<p:outputLabel value="#{street.postalCode}"/>
</p:column>
<p:column headerText="#{msgs['label.location']}">
<p:outputLabel value="#{street.locName}"/>
</p:column>
</p:dataTable>
in above datatable we have only radio selection, I need row selection too. please help
One thing to ALWAYS remember, is that client-side everything is html/javascript/css (with the help of jQuery in this case).
PrimeFaces 7.0 (and up) has a onRowClick attribute but I'm not sure if this is suitable for this case. A solution that always works (older and newer versions) is composed of several really simple steps (just remember the first statement in this answer)
Start with jQuery for getting Click event on a Table row. For this we need to check where the actual clicks need to be on. Using the id of the datatable for the first part is not a bad choice. But what is the id? Well, for this there also is a Q/A in Stackoverflow. How can I know the id of a JSF component so I can use in Javascript
For the PrimeFaces showcase this actually is #form\\:radioDT" But within that whole html structure, we only need to click on certain rows (e.g. not in headers or the likes). This therefor becomes something like
$("#form\\:radioDT").on("click", "tbody.ui-datatable-data tr", function() {
console.log(this);
});
If you try this in the PrimeFaces showcase, you'll see clicks on the row being logged (including clicks that originate on the radiobutton)
The next step would be to Simulate a click on 'a' element using javascript/jquery
But click where? Well, that can be easily found with the browser developer tools as well. Related to the tr that was clicked (this) it is:
$(this).find(".ui-selection-column .ui-radiobutton-icon").click(); //javascript version (selectors can be different, many work). Alternative is using jquery .trigger('click')
Combining this results in
$("#form\\:radioDT").on("click", "tbody.ui-datatable-data tr", function() {
console.log(this);
$(this).find(".ui-selection-column .ui-radiobutton-icon").click();
});
Which unfortunately results in a 'too much recursion' error. This is caused by the fact that a click on the radio button bubbles up to the row which triggers a click on the radiobutton which... you get the point.
So the first thought is to prevent clicks bubbling up. Unfortunately, it is not bubling up from our function but from the click on the radiobutto so we need to adapt our source. Again existing Q/A in stackoverflow help: jQuery trigger click gives "too much recursion"
What you do is check if the click on the row originated as a click on anything but the radiobutton. Only then simulate the click. For this we need the event and from the event the target and with the same selector as for the click we do a check:
$("#form\\:radioDT").on("click", "tbody.ui-datatable-data tr", function(event) {
if ( !$(event.target).is('.ui-selection-column .ui-radiobutton-icon')) {
console.log("Simulate click on radiobutton: ", event.target);
$(this).find(".ui-selection-column .ui-radiobutton-icon").click();
} else {
console.log("Skip: ", event.target);
}
}
);
Keep in mind that $("#form\\:radioDT") is based on the full client id of the datatable component in the PrimeFaces showcase. Replace it with yours.
How and where to add this to your page and how to re-add it in the case of ajax updates is also covered in Q/A in Stackoverflow.

Rendering a data-table only when the bean returns a list

I have been trying to render a rich:dataTable, but fails, when it comes to its conditional rendering.I wanted to render it only if the size of the list, the backing-bean fetches from DB, is greater than zero.
JSF-2.0, RichFaces-4 are what i use.
You have to use the "render" attribute of the datatable. With it you can define if the component is rendered to the client or not. So check by EL if the list is populated.
you can do something like:
rendered="#{not empty listObject}"
and all is fine.
I always implement my database query method to never return null, if the query has no result I return an empty list. This way i'm sure I never get a nullpointerexception and I prefer then to show an empty table. Because it's easer to layout the page, when you are sure the table always exist.
Hope that helps.
The scenario is I have a groupId which I rightclick on. On the context menu, I choose Display CTNs and it should then render all the CTNs of this group in a data-table. It starts with a JavaScript call, which I call once I choose "Display CTNs". It takes care of supplying the GroupId to the a4j:jsFunction.
<rich:dataTable value="#{ctnGrpMgmtController.ctnDetailsList}"
var="ctnVar" id="ctnTable" rows="5"
rendered="#{not empty ctnDetailsList}">
The above should be rendered after the below a4j:jsFunction executes.
<a4j:jsFunction name="selectGroupForManagingCtns"
action="#{ctnGrpMgmtController.loadCTNsForAGroup}"
render="ctnListPanel,ctnTable">
<a4j:param name="name"
assignTo="#{ctnGrpMgmtController.groupId}" />
</a4j:jsFunction>
I have to do an F5 to see the component "ctnTable", which is where the problem starts.
It looks like the attribute name on the a4j:jsFunction is reRender instead of just render. Should fix it.

A way to easily check if an element was rendered in jsf2

I have a button that is supposed to close a modal panel and render several elements.
Is there an easy way to check if an element was rendered after the button was pressed?
example button:
<a4j:commandButton onclick="#{rich:component('modal')}.hide();"
style="background-repeat:no-repeat;width:18px;height:18px;"
image="includes/images/close.gif"
render=":id1 :id2">
<f:setPropertyActionListener target="#{Controller.attribute}" value="false" />
</a4j:commandButton>
I have another button that does not "work" on the un-rendered div.
The conditon of the rendered attribute of all of the button's parent components must be preserved when you're submitting the form. So it should not only evaluate true during the request of presenting the form with the button, but it should also evaluate true during the request of processing the form submit. Easiest is to put the backing bean in the view scope by #ViewScoped.
Also, if you're re-rendering a component which in turn contains a <h:form>, you need to explicitly add the client ID of that <h:form> to the render attribute. E.g. when component with client ID id1 has in turn a <h:form id="formOfId1">.
render=":id1 :id2 :formOfId1"
You can debug this all by just exploring the HTTP traffic in the webbrowser's developer toolset (press F12 in Chrome/IE9/Firebug and check the "Network" section).
See also:
commandButton/commandLink/ajax action/listener method not invoked or input value not updated - Points 5 and 7.
I have hit the same problem. My solution was to display the current time inside the element that i want to be rendered.
If, for instance, you want to render a panel, just paste inside that panel the following snippet:
<h:outputText value="#{session.lastAccessedTime}">
<f:convertDateTime pattern="yyyy-MM-dd HH:mm:ss.SSS" type="date" />
</h:outputText>
If the time is updated after button click, then obviously the component was rendered. But is the opposite also true? If the time is not updated, does this mean the component was not rendered? What if the component got rendered properly, but the time displayed is frozen to the moment the page was first accessed? I had small doubts about that, so i refreshed the page, and the time changed. So the time displayed is not frozen to the initial moment.
Also note that there are multiple ways to get the time; if you don't like to use session time, you could set a property in the backing bean, and initialize it to new Date()

JSF: How to get the selected item from selectOneMenu if its rendering is dynamic?

At my view I have two menus that I want to make dependent, namely, if first menu holds values "render second menu" and "don't render second menu", I want second menu to be rendered only if user selects "render second menu" option in the first menu. After second menu renders at the same page as the first one, user has to select current item from it, fill another fields and submit the form to store values in database. Both the lists of options are static, they are obtained from the database once and stay the same. My problem is I always get null as a value of the selected item from second menu. How to get a proper value? The sample code of view that holds problematic elements is:
<h:selectOneMenu id="renderSecond" value="#{Bean.renderSecondValue}"
valueChangeListener="#{Bean.updateDependentMenus}"
immediate="true"
onchange="this.form.submit();" >
<f:selectItems value="#{Bean.typesOfRendering}" />
</h:selectOneMenu><br />
<h:selectOneMenu id="iWillReturnYouZeroAnyway" value="#{Bean.currentItem}"
rendered="#{Bean.rendered}" >
<f:selectItems value="#{Bean.items}" />
</h:selectOneMenu><br />
<h:commandButton action="#{Bean.store}" value="#Store" />
However, if I remove "rendered" attribute from the second menu, everything works properly, except for displaying the menu for all the time that I try to prevent, so I suggest the problem is in behavior of dynamic rendering. The initial value of isRendered is false because the default item in first menu is "don't render second menu". When I change value in first menu and update isRendered with valueChangeListener, the second menu displays but it doesn't initialize currentItem while submitting.
Some code from my backing bean is below:
public void updateDependentMenus(ValueChangeEvent value) {
String newValue = (String) value.getNewValue();
if ("render second menu" == newValue){
isRendered = true;
} else {
isRendered = false;
}
}
public String store(){
System.out.println(currentItem);
return "stored";
}
If you're using the rendered attribute on UIInput and UICommand components, then you have to make sure that the rendered condition evaluates exactly the same way in the subsequent request (during submitting the form) as it was during the initial request (during displaying the form). When it evaluates false, then the request values won't be applied/validated and the model values won't be updated and no action will be invoked for the component(s) in question.
An easy fix is to put the bean in the session scope, but this has caveats as well (poor user experience when opening same page in multiple browser tabs/windows). On JSF 2.0 you could use the view scope instead. On JSF 1.x you would use Tomahawk's t:saveState for this.
The problem arises only when we have "don't render" as a first option. I decided to change the values in the database and now I have "render second menu" as a first one. With such an approach, everything works fine.

Richfaces modal panel and a4j:keepAlive

I've got unexpected problems with richfaces (3.3.2) modal panel. When i try to open it, browser opens two panels instead of one: one is in the center, another is in the upper left corner. Besides, no fading happens. Also i have three modes: view, edit, new - and when i open my panel it should show either "Create new..." or "Edit..." in the header and actually it shows but not in the header as the latter isn't rendered at all though it should, because i set proper mode in action before opening this modal panel. Besides it works fine on all other pages i've made and there are tens of such pages in my application. I can't understand what's wrong here. The only way to fix it is to remove <a4j:keepAlive/> from the page that is very strange, imho.
I'm not sure if code will be usefull here as it works fine everywhere in my application but this only case. So if you put it on your page it will probably work without problems. My only question is: are there any hidden or rare problems in interaction of these two elements (<rich:modalPanel> and <a4j:keepAlive>)? Or shall i spent another two or three days searching for some wrong comma, parenthesis or whatever in my code? :)
For most curious. Panel itself:
<!-- there's no outer form -->
<rich:modalPanel id="panel" autosized="true" minWidth="300" minHeight="200">
<f:facet name="header">
<h:panelGroup id="panelHeader">
<h:outputText value="#{msg.new_smth}" rendered="#{MbSmth.newMode}"/>
<h:outputText value="#{msg.edit_smth}" rendered="#{MbSmth.editMode}"/>
</h:panelGroup>
</f:facet>
<h:panelGroup id="panelDiv">
<h:form >
<!-- fields and buttons -->
</h:form>
</h:panelGroup>
</rich:modalPanel>
One of the buttons that open panel:
<a4j:commandButton id="addBtn"
reRender="panelHeader, panelDiv"
value="#{form.add}"
oncomplete="#{rich:component('panel')}.show()"
action="#{MbSmth.add}"
image="create.gif"/>
Action invoked on button click:
public void add() {
curMode = NEW_MODE; // initial mode is VIEW_MODE
newSmth = new Smth();
}
Mode check:
public boolean isNewMode() {
return curMode == NEW_MODE;
}
public boolean isEditMode() {
return curMode == EDIT_MODE;
}
remember that the modalPanel is always there, but it's hidden. i think the keepAlive is showing this at all times.
Add domElementAttachment="parent" parameter to rich:modalPanel, and the editor will work fine.

Resources