Установить идентификатор в списке данных динамически без JavaScript

У меня большая проблема с интерфейсом Primefaces. Я хочу сделать цикл по списку и отобразить некоторую информацию + скрытое поле редактирования.

Фрагмент кода XHTML Primefaces:

<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.

Большое тебе спасибо!

ура, JohnRamb0r


person JohnRamb0r    schedule 10.01.2019    source источник
comment
В вашем образце вы создадите повторяющиеся идентификаторы edit_{data.id}. Может быть, это проблема. Вы получаете сообщения об ошибках? Если вам нужен идентификатор только для update=, вы должны поместить <span и <p:panelGrid внутри <h:panelGroup id='edit_#{data.id}'> и лишить их идентификаторов.   -  person Holger    schedule 10.01.2019
comment
Привет, спасибо за ваш ответ. span был просто примером, показывающим, что это решение работает, а часть panelGrid — нет. Мне нужна визуализированная опция для обновления PanelGrid, поэтому я хотел ее использовать.   -  person JohnRamb0r    schedule 10.01.2019
comment
Одна из проблем заключается в том, чтобы поместить идентификатор (обновления) в компонент, который имеет атрибут rendered. Если этот компонент не отображается, вы не можете использовать идентификатор. Как я уже писал: вы должны поместить PanelGrid внутри <h:panelGroup id='edit_#{data.id}'>, и никто другой не должен получать такой же идентификатор.   -  person Holger    schedule 10.01.2019
comment
Теперь я удалил тег span и завернул панель PanelGrid в группу panelGroup. Вывод также тот же: Cannot find component for expression "edit_1" referenced from "main:j_idt70:0:j_idt122". Заявление об обновлении ищет edit_1, но идентификатор панели сетки генерируется Primefaces: main:j_idt70:0:edit_. Но я хочу, чтобы у panelGrid был тот же идентификатор --› edit_1 для примера.   -  person JohnRamb0r    schedule 10.01.2019
comment
А, да, это внутри dataList. Поскольку dataList сам генерирует идентификаторы :0, вам не нужно использовать динамические идентификаторы. Просто используйте, например, editbox. Он будет изменен на main:j_idt70:x:editbox, где x — индекс строки списка данных. Родственник update='editbox' без : должен найти.   -  person Holger    schedule 10.01.2019
comment
да, но у меня есть много данных для отображения, и у всех данных есть одно скрытое поле редактирования. Итак, если я вызову update="editbox", то все поля редактирования будут всплывать - я прав?   -  person JohnRamb0r    schedule 10.01.2019
comment
Нет, только тот, что в том же namincontainer.   -  person Kukeltje    schedule 10.01.2019


Ответы (1)


Я бы сказал, что вы почти работаете против JSF и того, как он работает в вашем примере кода. Прежде чем показать вам рабочий пример, я хотел бы сказать несколько вещей, связанных с хорошей практикой:

  • Не вызывайте методы напрямую, используйте встроенный перевод ссылок на свойства. Ссылка на data.locked будет автоматически преобразована в вызов data.isLocked(). Вызов data.locked предпочтительнее, так как это заставит фреймворк оценить его вместо того, чтобы вы отправляли уже оцененное значение.
  • Работайте с JSF — не против. В вашем примере кода много ненужных идентификаторов и использование ненужных тегов и индексов. Будьте проще и работайте с фреймворком. Вместо того, чтобы ссылаться на идентификатор, ссылайтесь на объект напрямую — это упрощает код и упрощает его использование на самой странице.
  • Используйте действие в качестве основного исполнителя бизнес-логики и результата. Слушатели действий выполняются заранее и могут использоваться для перехвата или остановки выполнения основного действия. Поэтому они подходят для использования в качестве шага проверки перед выполнением бизнес-логики.
  • Отметьте свои события. Рекомендуется использовать соглашение об именах on<Something> при именовании методов, получающих пользовательские события. Это позволяет четко их идентифицировать.

Я сделал небольшой рабочий пример вашего кода (используется Lombok и 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;
    }
}

Приведенный выше код инициализирует список данных из десяти сущностей и заполняет его случайными данными. Я воспользовался привилегией изменить data на entity. Когда дело доходит до вашего HTML-кода, я чувствую, что его нужно немного почистить. Определение JSF будет выглядеть примерно так;

<?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>

Обратите внимание, как редактирование div/outputPanel заключено в другой контейнер (форму). Если бы мы пропустили упаковку и вместо этого отправили обновление непосредственно в обернутый контейнер, тег rendered контейнера никогда не обновлялся бы во время обновления, и поэтому div никогда не отображался бы. В данном конкретном случае именно поэтому форма определяется вне контейнера, а не внутри.

В этом примере используется bean-компонент @ViewScoped, поэтому, пока вы остаетесь на странице, резервные данные должны оставаться неизменными. Если вы перезагрузите страницу, вы получите новый набор данных с новыми объектами, так как это повторно инициализирует вспомогательный компонент и снова вызовет @PostConstruct.

См. также

person Adam Waldenberg    schedule 11.01.2019
comment
И вы не можете генерировать динамические идентификаторы во время рендеринга (и в этом редко есть необходимость) - person Kukeltje; 11.01.2019
comment
JSF уже делает это по умолчанию. Компонент всегда получает идентификатор "...sub-sub-component:sub-component:component". Кроме того, при использовании простых лиц вы можете воспользоваться преимуществами селекторов JQuery внутри ваших атрибутов update — иногда это может значительно упростить задачу. Однако в этом случае вам даже не нужно думать ни о чем из этого. Всегда следуйте принципу KISS, en.wikipedia.org/wiki/KISS_principle. - person Adam Waldenberg; 12.01.2019
comment
Я также хотел бы добавить это как связанный Q/A (stackoverflow.com/questions/9147771/) на этот вопрос отвечает, вы можете использовать JSTL (который работает во время сборки) для генерировать идентификаторы. Но для этого конкретного случая использования, пожалуйста, не делайте этого. - person Adam Waldenberg; 12.01.2019