0

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

Kukeltje
  • 12,223
  • 4
  • 24
  • 47
  • In your sample you'll produce duplicate ids `edit_{data.id}`. Maybe this is the problem. Do you get error messages? If you need the id only for the `update=` you should put the `` and strip the ids of them. – Holger Jan 10 '19 at 14:22
  • Hi, thx for your answer. the span was just an example to show that this "solution" works and the panelGrid part doesnt. I Need the rendered Option to update the panelGrid therefore i wanted to use it. – JohnRamb0r Jan 10 '19 at 14:29
  • One problem is, to place an (update)id in a component, which has a rendered attribute. If this component isn't rendered, you can't use the id. As I wrote: you should place the panelGrid inside a `` and no other has to get the same id. – Holger Jan 10 '19 at 14:34
  • Now I deleted the span tag and wrapped my panelGrid within a panelGroup. The Output is also the same: `Cannot find component for expression "edit_1" referenced from "main:j_idt70:0:j_idt122".` The update Statement is looking for "edit_1" but the id of the panelgrid is generated by Primefaces: "main:j_idt70:0:edit_". But I want that the panelGrid should have the same id --> edit_1 for the example. – JohnRamb0r Jan 10 '19 at 14:48
  • Uh, yes, it's inside the dataList. Because dataList generates ids by itself `:0` you don't need to use dynamic ids. Just use i.e. `editbox`. It will be changed to `main:j_idt70:x:editbox` where x is the row-index of the dataList. A relative `update='editbox'` without `:` should find it. – Holger Jan 10 '19 at 15:00
  • yeah but I have many datas to Display and every data has one hidden "editbox". So if I call `update="editbox"`then every editboxes will Pop up - Am i right? – JohnRamb0r Jan 10 '19 at 15:37
  • No, only the one in the same namincontainer. – Kukeltje Jan 10 '19 at 16:31

1 Answers1

2

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

Adam Waldenberg
  • 2,271
  • 9
  • 26
  • And you cannot generate dynamic id's at render time (nor is there rarely the need) – Kukeltje Jan 11 '19 at 18:32
  • 1
    JSF already does this by default. A component always gets the ID `"...sub-sub-component:sub-component:component"`. Also, when using primefaces, you can actually take advantage of JQuery selectors inside your `update` attributes - this can sometimes make things much, much easier. However, in this case, you don't even need to think about any of that. Always follow the KISS principle, https://en.wikipedia.org/wiki/KISS_principle – Adam Waldenberg Jan 12 '19 at 02:09
  • 1
    I would also like to add that as the linked Q/A (https://stackoverflow.com/questions/9147771/how-can-i-set-id-of-a-component-tag-inside-uirepeat) to this question answers, you can actually use JSTL (which operates during build time) to generate ID's. But for this particular use case, please don't do it. – Adam Waldenberg Jan 12 '19 at 02:36