How to group Radio Buttons in h:datatable jsf2.0 - jsf-2

I have a column of radio buttons in h:datatable in JSF2 but I can not find a way to group them. Meaning all of them can be selected at the same time where as whole point of radio button is so that only one can be selected at any given time. I am sure there will be standard way of doing it.
I am using myfaces. Can use richfaces if really needed to.
Can any one help with this.

When using standard JSF <h:selectOneRadio> component inside a <h:dataTable> you'll need to bring a shot of JavaScript into the game which unchecks all other radio buttons in the same column when one is checked.
<h:column>
<h:selectOneRadio onclick="uncheckOthers(this);">
</h:column>
with
function uncheckOthers(radio) {
var name = radio.name.substring(radio.name.lastIndexOf(':'));
var elements = radio.form.elements;
for (var i = 0; i < elements.length; i++) {
if (elements[i].name.substring(elements[i].name.lastIndexOf(':')) == name) {
elements[i].checked = false;
}
}
radio.checked = true;
}

First, it's ludicrous that something this simple in HTML or JSP is so hard to do in JSF. These added layers should be making life easier, not harder. But it seems to me they just make life harder. I used to like them. Now I am a Java Luddite, advocating now in favor of simpler approaches that include less in the way of "new cool-sounding 'technologies'" and more in the way of "stuff that just plain works without having to struggle all week to do something that used to take just 5 minutes". Anyway, I have tried the past 3 working days to get the simple functionality in JSF to work that causes other radio buttons to deselect and leave the user-selected one unchanged. I tried several approaches including a custom tag library ("overkill", defined) and a number of mixing and matching of stuff that only produced a lot of stacktraces. I finally fell back on Javascript and accepted that creating a reliably generalized solution was not likely. I have read similar complaints elsewhere. OK, my gripe session is over. Here's what I did to slay my particular JSF-radio-button dragon:
On the .xhtml page:
<ui:composition template="../template.xhtml"
...
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:a4j="https://ajax4jsf.dev.java.net/ajax"
...
>
<a4j:loadScript src="/resources/scripts/custom.js" />
...
<ui:param name="pageBean" value="#{recordsBean}" />
...
<h:dataTable id="tblMine"
value="#{pageBean.records}"
var="item"
border="1" celpadding="0" cellspacing="0"
style="font-size=10px;text-align: center;"
>
<h:column>
<f:facet name="header">
<h:panelGroup><h:panelGrid cellspacing="0" cellpadding="0" border="0"
id="rowSelect" columns="1" width="100%"><h:outputText
value="Select" /></h:panelGrid></h:panelGroup>
</f:facet>
<h:selectOneRadio id="userselectedrecord"
onclick="javascript:clearOtherRadioButtons(event);" >
<f:selectItem itemValue="#{item.recordid}"
itemLabel="" /> <!-- I set itemLabel to "" because in my case,
I don't want any text next to the radio btns -->
</h:selectOneRadio>
</h:column>
...
... and the rest of the page ...
In the custom.js file, I have:
function clearOtherRadioButtons(evnt)
{
// Have to try 'currentTarget', then 'srcElement'. Issue is Firefox
// supports 'currentTarget' but IE does not. IE supports 'srcElement'.
var btn = (evnt.currentTarget) ? evnt.currentTarget : evnt.srcElement;
var matchindexlocn = btn.id.indexOf('userselectedrecord');
var btnidstarttxt = btn.id.substring(0,
matchindexlocn + 18 ); // 'userselectedrecord' length = 18
var elementid = '';
var i = 0;
var ele = null;
var allElements = document.getElementsByTagName('*');
for(i = 0; i < allElements.length; i++)
{
elementid = allElements[i].id;
if ( (elementid.indexOf(btnidstarttxt) == -1) &&
(elementid.indexOf('_') > -1) )
{
try
{
ele = document.getElementById(elementid);
ele.checked = false;
} catch (e) {}
}
}
} // function clearOtherRadioButtons(evnt)
That's it. By way of some explanation re that if() in the Javascript:
elementid.indexOf(btnidstarttxt) == -1
--> This narrows the elements down to those that do not start with the JSF-added object ID text that is associated with the button group associated with the passed-in event. We do not want to unselect the button in the button group whose event was passed into the function.
elementid.indexOf('_') > -1
--> This narrows the elements down further to only actual buttons since JSF adds underscores to the button element IDs.
The reason the code that sets checked = false is in a try..catch block is just in case something goes wrong with any presumptions around the logic and the circumstance is not handled. If you know the environment and can control the element ids, you should be fine. However on the off chance that something changes due to, let's say for example, the app's host server getting some new version JSF library installed that overrides your app's classloading policy (in a work environment, these things as we know can happen beyond our control) that now assigns these element ids differently, the app won't break with a sudden nasty Javascript error or worse yet, the radio button selection just stops working. Worst I guess that can happen is that multiple radio btns can be selected at the same time and the user can submit them. I suppose a good precaution to take is to add yet more Javascript code to validate that only one option is being submitted by the user, or validate the user's submission server-side.
See, way too much work just to manage radio button state.

Related

ui:repeat in o:tree not working as expected

I'm using a omnifaces o:tree of "branches" where each branch has a "list of leafs" with the attribute "color" which should be editable in the tree.
- branch 0
- leaf 0 (color = "green")
- leaf 1 (color = "yellow")
- branch 1
- leaf 0 (color = "purple")
- branch 1_0
- leaf 1 (color = "red")
- leaf 2 (color = "orange")
- leaf 3 (color = "brown")
Adding/removing branches and adding leafs to any branch works fine and as expected. Also the rendering of any complex tree including all the list of leafes (displaying of the color attribute with the correct value) works like a charme.
But changing the color or removing a leaf (so whatever is within the ui:repeat) only works as expected for the last rendered branch.
The "color" input fields for the other leafes/branches don't work at all and the delete leaf link within the ui:repeat also doesn't work as for the leaf attribute in removeLeaf(...) always the corresponding leaf from the last rendered branch gets passed.
So in the above example clicking on delete for the yellow leaf from branch 0 will call removeLeaf(orange, branch 0) which will then oviously not delete anything as branch 0 has no orange leaf.
This is the code strapped down to the essential part - anything else can be provided if needed:
<h:form>
<o:tree value="#{treeBean.tree}" var="branchEntity" varNode="branchNode">
<o:treeNode>
<o:treeNodeItem>
<ui:repeat value="#{branchEntity.leafList}" var="leaf">
<h:panelGrid columns="2">
<p:inputText value="#{leaf.color}" />
<p:commandLink action="#{treeBean.removeLeaf(leaf, branchEntity)}" styleClass="ui-icon ui-icon-trash"
process="#form" update="#form" />
</h:panelGrid>
</ui:repeat>
<p:commandLink action="#{treeBean.addLeaf(branchEntity)}" styleClass="ui-icon ui-icon-plus"
process="#form" update="#form" />
<o:treeInsertChildren />
</o:treeNodeItem>
</o:treeNode>
</o:tree>
<p:commandButton id="save" value="Save" action="#{treeBean.save}" process="#form" update="#form" />
</h:form>
I know that there is some issue with nested ui:repeat but as I'm on Mojarra 2.1.19 I guess it's not the case here. And actually nesting two ui:repeats works fine if I replace o:tree by a ui:repeat which iterates over a list of branches. But then I obviously lost the tree functionality which I need. This I just tested to verify nested ui:repeats work properly.
This problem seems to be similar to another question but it's not exactly the same...
If I replace the the inner ui:repeat by a p:dataList (as suggested in some other answers to solve ui:repeat problems and also now in BalusC answer here) the removeLeaf() links will all work proper but still only color input fields of the last branch will be bound.
UPDATE: I now had a closer look when using the p:dataList. The POST content from the AJAX request which I got from my browsers developer tools looks good:
javax.faces.partial.ajax=true
javax.faces.source=hForm:save
javax.faces.partial.execute=hForm
javax.faces.partial.render=hForm
hForm:save=hForm:save
hForm=hForm
hForm:oTree:0:pDataList:0:color=green
hForm:oTree:0:pDataList:1:color=yellow
hForm:oTree:1:pDataList:0:color=purple
hForm:oTree:1_0:pDataList:0:color=red
hForm:oTree:1_0:pDataList:1:color=orange
hForm:oTree:1_0:pDataList:2:color=brown
javax.faces.ViewState=-6137230173999059936:-6718691551411872927
The treeBean.save() method is just logging the whole Tree.toString() to console which ends up like this:
[Branch [leafList=[Leaf [color=null], Leaf [color=null]]],
Branch [leafList=[Leaf [color=null]]]
[Branch [leafList=[Leaf [color=red], Leaf [color=orange], Leaf [color=brown]]]]]
If the color had some value != null before, the value remains the same - so it doesn't get overriden by null. I'm not yet deep enough into JSF to really know how to investigate where the information gets lost on it's way.
(I initially used Mojarra 2.1.19 on JBoss EAP 6.2 + Primefaces 5.2 + Omnifaces 2.1 but also experience exactly the same on Mojarra 2.2.12, TomEE, Primefaces 5.3 + Omnifaces 2.2. The complete Maven Eclipse sample project can be found here I added even one more simple String attribute (no List) directly to the Branch which works fine.)
This is indeed caused by a state management bug in <ui:repeat>. It didn't properly recognize itself being nested in another iterator and stubbornly clears out the state before being revisited during each iteration of the parent iterator. It only recognizes a parent UIRepeat or UIData, but that's it. The <o:tree> doesn't and can't extend from UIData, and the UIRepeat is unfortunately a implementation-specific class and thus can't be used publicly as long as you want to be independent from a specific JSF implementation (Mojarra, MyFaces, etc).
Your best bet is to replace <ui:repeat> by a real UIData component which has a much better state management implementation. I just tried it here and adding items works just fine for me with <h:dataTable> and <p:dataList>. Only removing items failed with <h:dataTable>, because it rendered wrong client IDs. It's not taking into account the container client ID. The <p:dataList> worked for me in both cases of adding and removing items. Still updating the server side model of any bound input fields in the list fails as discussed in detail in the comments of this answer.
Replacing the entire <ui:repeat> by a <p:dataList type="none"> (without type, it will render an <ul><li>) is the closest you can get.
<p:dataList type="none" value="#{branchEntity.leafList}" var="leaf">
...
</p:dataList>
World would have been much simpler if JSF offered a public UIRepeat-like (marker) interface which all those repeaters could just check for when they're about to manage the state.

Expanding behavior of Richfaces Collapsible Panel

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>

Drag and drop of rows primefaces datatable/datagrid?

I trying to drag and drop of rows using primefaces datatable/datagrid.But this is issue in primefaces.What is best way to integrate drag and drop of primefaces datatable/datagrid with jquery or other third party api plugin.Please suggesst to me.
You need to add some JavaScript (JQuery) in your JSF page to make Datatable sortable and to disable selection mode. Listed below doReorder() method gets the new order of the rows and save to order_q variable:
<script type="text/javascript">
$(document).ready(function () {
$('.ui-datatable tbody').sortable();
$('.ui-datatable tbody').disableSelection();
});
function doReorder() {
var order = '';
var len = $('.row').length;
$('.row').each(function (index) {
order += ($(this).text() + ((index == (len - 1)) ? '' : ';'));
});
$('#order_q').val(order);
return true;
}
</script>
Add some code to your Datatable component. You see a new column with number of the row as value with attribute styleClass set to „row” . There is a hidden variable order_q updated during execution JavaScript method doReorder() :
<p:dataTable rowIndexVar="rowIndex"
var="entry" value="#{myBean.list}" >
<p:column headerText="#">
<h:outputText styleClass="row" value="#{rowIndex}"/>
</p:column>
... (other colums as you wish)
</p:dataTable>
<h:inputHidden id="order_q" value="#{myBean.order}"/>
<p:commandButton value="Submit order" ajax="false" onclick="return doReorder()" action="#{myBean.reorder}"/>
Your bean class should contain a String field order (with getters and setters of course). The variable wil store a value from the JavaScript – the new order of our list (source data of the Datatable widget). We need to implement also a method mentioned in the xhtml - reorder() to handle action from button „Submit order”
public String reorder() {
Map < String, String > tmp = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap();
String order = tmp.get("order_q");
if (order != null && !order.isEmpty()) {
ArrayList < YourData > reordered = new ArrayList < > ();
for (String s: order.split(";")) {
try {
Integer i = Integer.parseInt(s);
YourData data = list.get(i);
reordered.add(data);
} catch (NumberFormatException nfe) {}
}
list = reordered;
}
return null;
}
That’s all! Now you can reorder your Datatable using drag&drop feature and save the new order by clicking a button. I leave a reference to my blog where all of this is described:
http://michalu.eu/wordpress/drag-and-drop-rows-to-reorder-your-datatable-in-primefaces/
Just use
<p:dataTable draggableRows="true"
On Primefaces 5.0 DataTable documentation you can see the parameter draggableRows, like this :
draggableRows | false | Boolean | When enabled, rows can be reordered using dragdrop.
http://www.primefaces.org/documentation
As far as i know from datatables (primefaces) in my applications, they have 2 main states/modes. It may be not the correct word but i stay with it. The 1st is display only, and 2nd editing.
(In display i dont see events triggering so DnD wont work.) in editing they have a serious issue with selectivity. u have to press at the edge of the table and not in inline components reach to get the focus on the row. So The drop must be done in that area and only propably for events to trigger. If u see the examples...all drops are made on an outer component(panel etc) and the table is just updated because the ondrop event in ajax has run and changed the data the drop table will contain. That may be magic for incrementing data...but not for rearranging and updating cell or row data.
SO the answer for that up to primefaces 3.5.x is i dont think you can achieve it...I dont think you can doit with jquery either because they run client side? i havent used them before so i cant tell for sure.
A bit of rework of your forms may be needed.

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.

Dynamically added input field in ui:repeat is not processed during form submit

I am trying to make a input form for answers in my application and I start with four "empty" answers which the view loops over and make input fields for. I have an add answer button which I add one question to the array of answers and then the view render the answers again, but now with an additional input field. The backing bean is viewscoped. However if I submit the form without pressing the add answer button it all works. The data is saved in the database. But if I add an answer after the four is filled out the last one does not get the data from the inputfield (answer.description). If I press the add answer first (without filling out any input fields) the data from the fields are not captured at all leaving all 5 empty so no data is saved in the database.
I have this in the form:
<ui:repeat var="answer" value="#{bean.answers}">
<div class="field">
<h:outputLabel for="answerAlternative-#{answer.serialNumber}"
value="Svaralternativ #{answer.serialNumber}" />
<h:inputText id="answerAlternative-#{answer.serialNumber}"
value="#{answer.description}" size="40" />
</div>
</ui:repeat>
This is the method for creating a new input field:
public String addAnswer() {
if (answers.size() + 1 < 6) {
Answer answer = new Answer();
answer.setSerialNumber(answerSerialNumber + "");
answerSerialNumber++;
answers.add(answer);
}
return null;
}
Used for initializing the answers array with four empty input fields:
#PostConstruct
public void initBean() {
answers = new ArrayList<Answer>();
for (int i = 0; i < 4; i++) {
addAnswer();
}
}
This look to match the current problems of <ui:repeat> in Mojarra. It is totally broken in Mojarra.
You have basically 2 options:
Replace Mojarra by MyFaces which has a way more stable implementation of <ui:repeat>.
Use an UIData component instead of <ui:repeat>, e.g. <h:dataTable>, Tomahawk's <t:dataList>, PrimeFaces' <p:dataList>, etc.

Resources