I would say you are nearly working against JSF and the way it works in your code example. Before showing you a working example, there are a few things I would like to say here related to good practice:
- Do not call methods directly, use the built in translation of property references. A reference to
data.locked
will be translated to a call to data.isLocked()
automatically. A call to data.locked
is preferred, as it will cause the framework to evaluate it instead of you sending in the already evaluated value.
- Work with JSF - not against it. There are a lot of unnecessary ids and use of unneeded tags and indexes in your example code. Keep it simple and work with the framework. Instead of referencing an id, referencing the object directly - it simplifies the code and makes it easier to use on the page itself.
- Use action as the main executor of business logic and outcome. Action listeners are executed beforehand and can be used to intercept or stop the execution of the main action. They are are therefore suitable to be used as a validatory step before executing business logic.
- Mark your events. It's good practice to use the naming convention
on<Something>
when naming methods that receive user events. This allows you to clearly identify them.
I made a small working example of your code (this uses Lombok and Apache Commons);
@Data
@Named
@ViewScoped
public class DataListViewBackingBean implements Serializable {
private Entity entity;
private Entity selectedEntity;
private List<Entity> dataEntities;
@PostConstruct
private void init() {
dataEntities = new ArrayList<>();
for (int i = 0; i < 10; i++) {
dataEntities.add(new Entity(i, RandomUtils.nextBoolean(),
RandomStringUtils.randomAlphabetic(30)));
}
}
@Data
@AllArgsConstructor
@EqualsAndHashCode(exclude = {"locked","description"})
public class Entity {
private int id;
private boolean locked;
private String description;
}
public void onEdit(Entity entity) {
selectedEntity = entity;
}
public void onDelete(Entity entity) {
dataEntities.remove(entity);
selectedEntity = null;
}
}
The code above initializes a data list of ten entities and fills this with random data. I took the privilege to change data
to entity
. When it comes to your HTML code, I felt it needed some cleaning. The definition of the JSF would look something like this;
<?xml version="1.0" encoding="UTF-8"?>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:p="http://primefaces.org/ui">
<h:head>
<title>Data list test</title>
</h:head>
<h:body>
<h:form id="items">
<p:dataList type="definition" value="#{dataListViewBackingBean.dataEntities}" var="entity">
<div class="ui-g">
<div class="ui-g-8">
#{entity.description}
</div>
<div class="ui-g-4" style="text-align: right">
<p:commandButton value="Edit" disabled="#{entity.locked}" action="#{dataListViewBackingBean.onEdit(entity)}" update=":edit" />
<p:commandButton value="Delete" disabled="#{entity.locked}" action="#{dataListViewBackingBean.onDelete(entity)}" update="@form :edit" />
</div>
</div>
</p:dataList>
</h:form>
<h:form id="edit">
<p:outputPanel rendered="#{dataListViewBackingBean.selectedEntity != null}">
<h1>Editing...</h1>
<p:inputText placeholder="Description" value="#{dataListViewBackingBean.selectedEntity.description}" />
<p:commandButton value="Save" update=":items" />
</p:outputPanel>
</h:form>
</h:body>
</html>
Note how the editing div/outputPanel
is wrapped in another container (a form). If we skipped the wrapping and instead pushed an update on the wrapped container directly, the rendered
tag of the container would never be updated during a refresh and the div would therefore never show up. In this particular case, this is why the form is defined outside the container instead of inside.
This example uses a @ViewScoped
bean, so as long as you stay on the page, the backing data should stay the same. If you reload the page you will get a new data set with new entities, as this will reinitialize a backing bean and call @PostConstruct
again.
See also