JSF 2 - Issue with custom input text component inside ui:repeat - jsf-2

I need your help..., please...
I'm in the middle of a migration process from JSF 1.2 to JSF 2.1.
The Implementation I use is Sun's Mojarra implementation with facelets, which in JSF 1.2 were not part of the standard but, since JSF 2.0 they become.
Right now I am using javax.faces.PARTIAL_STATE_SAVING as true. (Tried with false, but none of my custom components worked.)
We have some custom JSF components, and in most cases they are working fine. However, the following example is not working correctly:
<ui:repeat value="#{myBean.list}" var="item">
<tr style="cursor:pointer;">
<td class="texttable col_med col15Personal">
<bf:TextBox
compId="aliasName"
value="#{item.aliasName}"
compStyle="font-size:1em;"
maxLength="15" showAutoLabel="false"/>
</td>
</tr>
</ui:repeat>
bf:TextBox is a custom text box which extends HtmlPanelGroup and has 2 custom children components:
A custom label (extending HtmlOutputlabel) and aa custom input(extending HtmlInputText)
When this component is used without ui:repeat, everything works fine. However when inside it, problems occur.
In the first renderreponse phase, everything is fine. But when you do a post, in the restoreview phase, the children of the custom HtmlPanelGroup (label and inputText) are not present in the viewRoot, so the values submitted are not present.
The same code snipped was working in the JSF 1.2 correctly.
I know that there are alternatives to ui:repeat (example c:forEach), but since I'm working on a migration process it's really complex to change all code involving this type mentioned.
Thank you all in advance,

I have found a workaround for my issue. As the main goal was to make minimum changes into application... I avoided changing the all pages that were using ui:repeat. Instead, I have changed my custom component to save the children into State Holder (saveState method) and then I restore the children if the component has a ui:repeat as parent.
if (getInsideUIRepeat() != null && getInsideUIRepeat().booleanValue()) {
values[42] = this.getChildren();
}
this is extracted from saveState method.
if (getInsideUIRepeat() != null && getInsideUIRepeat().booleanValue()) {
List<UIComponent> children = (List<UIComponent>) values[42];
if (children != null) {
this.getChildren().clear();
this.getChildren().addAll(children);
}
}
this is extracted from restoreState method.
private boolean hasUIRepeatAsParent() {
UIComponent parent = this.getParent();
while (parent != null) {
if (parent instanceof UIRepeat) {
return true;
}
parent = parent.getParent();
}
return false;
}
this is the method that checks if component has UIRepeat as parent
Thank you all again...

Related

h:commandLink inside t:dataTable not working after JSF 2.1 upgrade

We have an issue with the h:commandLink inside t:dataTable, which are not working anymore after upgrading to JSF 2.1 (Mojarra 2.1.28), Tomahawk20-1.1.14, PrimeFaces 5.2. (The old stack was JSF 1.2, Tomahawk-1.1.10, RichFaces 3.4). The issue is action & actionListener methods do not get invoked at all. It submits the form; but remders the same page again.
Basically it's a custom component for context menu - it's a row specific context menu, that is shown after an icon in each row is clicked by user. The menu items are generated programmatically as below -
private List<HtmlCommandLink> createMenuItems(FacesContext facesContext) {
List<HtmlCommandLink> menuItems = new ArrayList<HtmlCommandLink>();
...
ValueExpression actionsVE = extractActionsValueExpression();
Set<ActionDefinition> menuOptions = (Set<ActionDefinition>)actionsVE.getValue(facesContext.getELContext());
...
for (ActionDefinition selectItem : menuOptions) {
HtmlCommandLink menuItem = new HtmlCommandLink();
menuItem.setValue(selectItem.getLabel());
menuItem.addActionListener(new SetPropertyActionListener());
String actionEl = String.format("#{%s}", selectItem.getActionValue());
menuItem.setActionExpression(createMenuItemActionMethodExpression(facesContext, actionEl));
menuItems.add(menuItem);
}
...
return menuItems;
}
Then this list is added to the menu panelGroup, in encodeBegin() method -
panelGroup = new HtmlPanelGroup();
getChildren().add(panelGroup);
icon = createIcon(panelGroup);
panelGroup.getChildren().add(icon);
...
menu = new HtmlPanelGroup();
...
menu.getChildren().addAll(createMenuItems(facesContext));
menu.setId(generateId());
menu.setOnmouseover("clear_popup();");
menu.setOnmouseout("close_timer();");
panelGroup.getChildren().add(menu);
...
icon.setOnmousedown("open_popup('" + menu.getClientId(facesContext) + "', this);");
icon.setOnmouseout("close_popup();");
...
I have checked Stackoverflow for similar issues and all relevant pre-requisites have been verified (there is one single form; the list is created in PostConstruct). Also have checked BalusC's this post - commandButton/commandLink/ajax action/listener method not invoked or input value not updated; and have ensure all points mentioned.
Here is the generated HTML for reference -
Create Letter
(Also, this is old HTML on JSF 1.2 -
<a class="context-menu-row" onclick="mojarra.jsfcljs(document.getElementById('form'),{'form:myDataTable:0:j_id126':'form:myDataTable:0:j_id126'},'');return false" href="#">Create Letter</a>
).
Any help / pointers to resolve it will be appreciated.
Note:
1. This custom context menu component is part of a larger framework, and so simply rewriting it using p:contextMenu is not a viable option.

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.

How to detect a field value change in a JSF 2.0 page

I am using JSF 2.0 to develop a pretty big and complex page which contains numerous fields. Thre would be quit command button at the buttom of the page and when ever user selects the quit option I need to detect whether user has entered any value on one of the fields of the page.
I am using the null check of each field values in the backing bean to do that now, but that's a very tedious and repeative job. I was wondering is there any smart solution for that ?? Any help will be highly appreciated.
Thanks in advance.
For that the valueChangeListener attribute is meant to be used.
<h:inputText ... valueChangeListener="#{bean.changed}" />
with
public void changed(ValueChangeEvent event) {
this.changed = true;
}
Your field-values are probably linked to properties of the backing-bean. When the value is changed, the setter is invoked. Inside the setter you could set a boolean field of the bean to true, if the value actually changed.
public void setPropertyX(Type newValue) {
if(!newValue.equals(this.X)) {
this.X = newValue;
this.fieldChanged = true;
}
}
For this to work, you backing-bean should be at least in #ViewScope.

Resources