JohnRamb0r
JohnRamb0r

Reputation: 1

Set ID within datalist dynamically without JavaScript

I have a big problem with my Primefaces Interface. I want to do a loop over a list and display some information + a hidden editfield.

XHTML Primefaces codesnippet:

<p:dataList  value="#{datas}" var="data">
<div class="ui-g">
    <div class="ui-g-3">
        <h2>#{data.desc}</h2>
    </div>
    <div class="ui-g-3">
        <p:commandButton operation="edit" disabled="#{data.isLocked()}" actionListener="#{view.edit(data)}"
            style="width:120px;" update="edit_#{data.id}" />
        <p:commandButton operation="delete" actionListener="#{view.delete(data.getId())}" disabled="#{data.isLocked()}"/>           
    </div>
</div>

<!-- works perfectly to set the id -->
<span id="edit_#{data.id}">#{data.desc} #{index}</span>

<!-- doesnt work - maybe of the rendering moment to set the id? -->
<p:panelGrid id="edit_#{data.id}" rendered="#{view.edit}">
    <p:outputLabel for="desc" value="#{msg.text}" />
    <p:inputText id="desc" value="#{view.selectedValue.desc}" />        
</p:panelGrid>

How can I set a dynamic ID to the panelGrid to update it by commandButton click if I want to edit that div? + How can I make the div toggled while editing it? or are there other Solutions? I am not allowed to use JavaScript/jQuery.

Thank you very much!

cheers, JohnRamb0r

Upvotes: 0

Views: 579

Answers (1)

Adam Waldenberg
Adam Waldenberg

Reputation: 2321

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

Upvotes: 2

Related Questions