Reputation: 27
This is my problem. I have two <p: commandLink>
inside a <ui: repeat>
.
I need to update these buttons by simulating checkbox when I click them.
Updating a <panelGroup>
around <ui:repeat>
works. However for a list with many items, after clicking the button the update, it makes the screen go back to the top of the list making it impracticable to select many items. Are there any ways to give an update specific line in <p:commandLink>
?
<tbody>
<ui:repeat value="#{myBean.itens}" var="item" varStatus="index">
<tr class="myCustomClass" id="item#{item.id}">
<th>
<p:commandLink
rendered="#{myConditionRendered}"
actionListener="#{myBean.deselect(item)}"
update=":panelGroupArroundId"
process="@this">
<i style="font-size: 15px;" class="fa fa-check-square-o" />
</p:commandLink>
<p:commandLink
rendered="#{!myConditionRendered}"
actionListener="#{myBean.select(item)}"
update=":panelGroupArroundId"
process="@this">
<i style="font-size: 15px;" class="fa fa-square-o" />
</p:commandLink>
</th>
<td>
<h:outputText value="#{item.field1}"/>
</td>
...
</tr>
</ui:repeat>
</tbody>
JSF version: 2.1
Primefaces version: 3.5
Upvotes: 2
Views: 818
Reputation: 2321
The trick here is to update only the sections you need - if you update the whole container the browser will reset and refresh from the point where the most recent update happened. In JSF there are a couple of ways to accomplish what you need. More recent versions of JSF (2.3 in particular) has selectors available such as :parent
to allow to easier target specific parts of the page to update. With 2.1 you are slightly more limited.
I made a quick test and came up with a working example of your particular user case. The image below is using Font Awesome and PrimeFaces 3.5;
I slightly tweaked your code and made a complete and working example. The controller and backing bean have been separated into separate classes in this example. First let's take a look at the backing bean:
@Data @Named @ViewScoped
public class MyBean implements Serializable {
@Data @RequiredArgsConstructor
public static class Item {
@NonNull private String name;
private boolean selected;
}
private List<Item> items;
@PostConstruct
private void init() {
items = List.of(new Item("a"), new Item("b"), new Item("c"),
new Item("d"), new Item("e"), new Item("f"), new Item("g"),
new Item("h"), new Item("i"), new Item("j")
);
}
}
This defines our data set and uses Lombok to minimize the amount of boilerplate code (@Data
and @RequiredArgsConstructor
). I also added a selected flag to keep track of if the checkbox is selected or not. This is then used in the controller bean when selecting and deselecting:
@Data @Named @RequestScoped
public class MyBeanController {
@Inject
private MyBean myBean;
public void select(Item item) {
item.setSelected(true);
}
public void deselect(Item item) {
item.setSelected(false);
}
}
Furthermore, here is the view (XHTML) definition:
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://xmlns.jcp.org/jsf/html" xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:p="http://primefaces.org/ui">
<h:head>
<title>Button update in UI repeat example</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.0.0/css/font-awesome.css" />
</h:head>
<h:body>
<table>
<tbody>
<ui:repeat value="#{myBean.items}" var="item">
<tr style="float: left; padding-right: 10px">
<th>
<h:form>
<h:panelGroup id="content">
<p:commandLink rendered="#{item.selected}"
actionListener="#{myBeanController.deselect(item)}"
update="content" process="@this">
<i class="fa fa-check-square-o" />
</p:commandLink>
<p:commandLink rendered="#{!item.selected}"
actionListener="#{myBeanController.select(item)}"
update="content" process="@this">
<i class="fa fa-square-o" />
</p:commandLink>
</h:panelGroup>
</h:form>
</th>
<td><h:outputText value="#{item.name}"/></td>
</tr>
</ui:repeat>
</tbody>
</table>
</h:body>
</html>
As you can see we wrap the buttons inside a form and define an container content
that we reference in the update
attributes of the p:commandLink
tags. Because we are wrapped inside a form the actual id of the panelGroup actually becomes something like j_idt6:9:j_idt8:content
with each content
id being unique (because each form has a unique id generated).
This allows us to target and update the data of that row specifically.
Theoretically you could update
the form directly without wrapping it inside a content
. However, doing so interferes with the life-cycle and handling of the form when wrapped inside a ui:repeat
. Adding an additional panelGroup
and updating that instead, like in this example, solves the issue.
Upvotes: 2