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

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.

Related

Primefaces autoUpdate only works once

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.

Primefaces multiple select checkbox bean values in Javascript function

I have a primefaces multiple select table populated from a backing bean. I am using the table.getSelectedRowsCount() javascript widget var to validate atleast one selection and it is working fine.
I now want to throw up a javascript confirmation dialog based on the state of the selected rows. for ex I have a button at the bottom of the table for printing the details of selected rows. I need to check a certain field of the bean and then allow printing.
IN pseudo code
if (#{bean.isComplete})
allowPrinting();
else
// alert user that this row is not yet in complete state
Update and panelGroup on button ajax event as shown in follow.
<h:panelGroup id="validateRerpot">
<h:panelGroup rendered="#{bean.isComplete}">
<script type="text/javascript">alert('show Dialog inplace');</script>
</h:panelGroup>
</h:panelGroup>
Try to update validateReport panel

In-cell editing using Primefaces 3.1 datatable

I am trying to use in-cell editing feature of datatable using Primefaces 3.1. The example shown on their site does not show how could I use "delete" functionality. When I click on "edit" image to make changes in the table row, it works. However, nothing happens when I click on the "delete" image.
I took the code from a working demo availble at PrimeFaces showcase. But, this demo also does not work for "delete".
However, nothing happens when I click on the "delete" image.
It is not clear what is "when I click on the delete image". In the Primefaces showcase there is no delete image (maybe you mix it with the cross icon, but this is for discarding changes).
I think you need to add delete functionality yourself, e.g. in a separate column (example as addition to the Primefaces showcase code):
<p:column>
<h:commandButton value="Delete" action="#{tableBean.delete(car)}"/>
</p:column>
and in the backing bean:
public void delete(Car car) {
carsSmall.remove(car);
}

How to do multiple selection in jsf or primefaces dataTable?

I want to try out building a simple grid that has a delete column, consisting of checkboxes, just like the usual one in email.
User can then tick the checkboxes, and press delete, and i can get all the checked records, and delete them one by one.
Im trying to find a way to achieve this, but so far im still in doubt.
These are what i have in mind, each with it's own implementation question, haha :
How to get the checked row indexes ? Using actionlistener for each toggle on each checkbox ? (but how do i pass the clicked index to the actionlistener ?)
Or is there a way where i can get all the grid model, and loop the data to find out which one is checked, just like swing ? (but how do i get the grid model in the jsf bean ?)
Or perhaps i should bind them to a simple list that contains only the checkbox column data ? (but how do i bind each checkbox to the list using indexes ?)
Im currently using primefaces, but i think the JSF solution can also be applied to primefaces datatable.
Please share your thoughts on this !
Thank you !
Isn't this example from Primefaces showcase exactly what you are looking for?
It looks that it is simply adding a column to the p:dataTable this way:
<p:dataTable var="item" value="#{yourBean.allElements}"
selection="#{yourBean.selectedElements}">
<p:column selectionMode="multiple" />
... other columns
</p:dataTable>

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.

Resources