【问题标题】:Set ID within datalist dynamically without JavaScript在没有 JavaScript 的情况下动态设置数据列表中的 ID
【发布时间】:2023-03-23 07:15:02
【问题描述】:

我的 Primefaces 界面有一个大问题。我想对列表进行循环并显示一些信息+隐藏的编辑字段。

XHTML Primefaces 代码n-p:

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

如果我想编辑该 div,如何为 panelGrid 设置动态 ID 以通过 commandButton click 更新它? + 如何在编辑时切换 div?还是有其他解决方案?我不允许使用 JavaScript/jQuery。

非常感谢!

干杯, JohnRamb0r

【问题讨论】:

  • 在您的示例中,您将生成重复的 ID edit_{data.id}。也许这就是问题所在。您收到错误消息吗?如果您只需要 update= 的 id,则应将 &lt;span&lt;p:panelGrid 放在 &lt;h:panelGroup id='edit_#{data.id}'&gt; 中并去掉它们的 id。
  • 您好,谢谢您的回答。跨度只是一个示例,表明此“解决方案”有效,而 panelGrid 部分无效。我需要渲染选项来更新 panelGrid,因此我想使用它。
  • 一个问题是,将(更新)id 放置在具有渲染属性的组件中。如果此组件未呈现,则无法使用 id。正如我所写:您应该将 panelGrid 放在 &lt;h:panelGroup id='edit_#{data.id}'&gt; 中,而其他人不必获得相同的 id。
  • 现在我删除了 span 标签并将 panelGrid 包装在一个 panelGroup 中。输出也相同:Cannot find component for expression "edit_1" referenced from "main:j_idt70:0:j_idt122". 更新语句正在寻找“edit_1”,但面板网格的 id 由 Primefaces 生成:“main:j_idt70:0:edit_”。但我希望 panelGrid 应该具有相同的 id --> 例如 edit_1 。
  • 嗯,是的,它在dataList里面。因为 dataList 自己生成 id :0 你不需要使用动态 id。只需使用即editbox。它将更改为main:j_idt70:x:editbox,其中 x 是 dataList 的行索引。没有: 的亲戚update='editbox' 应该会找到它。

标签: jsf primefaces


【解决方案1】:

我会说你几乎是在反对 JSF 以及它在你的代码示例中的工作方式。在向您展示一个工作示例之前,我想在这里说几件与良好实践相关的事情:

  • 不要直接调用方法,使用属性引用的内置翻译。对data.locked 的引用将自动转换为对data.isLocked() 的调用。首选调用data.locked,因为它会导致框架对其进行评估,而不是您发送已评估的值。
  • 使用 JSF - 不要反对它。在您的示例代码中有很多不必要的 id 和不需要的标签和索引的使用。保持简单并使用框架。无需引用 id,而是直接引用对象 - 它简化了代码并使其更易于在页面本身上使用。
  • 将操作作为业务逻辑和结果的主要执行者。动作侦听器是预先执行的,可用于拦截或停止主要动作的执行。因此,它们适合用作执行业务逻辑之前的验证步骤。
  • 标记您的活动。在命名接收用户事件的方法时,最好使用命名约定on&lt;Something&gt;。这样您就可以清楚地识别它们。

我为您的代码制作了一个小型工作示例(使用 LombokApache 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 将永远不会出现。在这种特殊情况下,这就是为什么在容器外部而不是内部定义表单的原因。

此示例使用@ViewScoped bean,因此只要您停留在页面上,支持数据应该保持不变。如果您重新加载页面,您将获得一个包含新实体的新数据集,因为这将重新初始化一个支持 bean 并再次调用 @PostConstruct

另请参阅

【讨论】:

  • 而且你不能在渲染时生成动态ID(也很少需要)
  • JSF 默认已经这样做了。组件始终获取 ID "...sub-sub-component:sub-component:component"。此外,当使用 primefaces 时,您实际上可以利用 update 属性中的 JQuery 选择器 - 这有时可以使事情变得更加容易。但是,在这种情况下,您甚至不需要考虑任何这些。始终遵循 KISS 原则,en.wikipedia.org/wiki/KISS_principle
  • 我还想补充一点,作为该问题答案的链接 Q/A (stackoverflow.com/questions/9147771/…),您实际上可以使用 JSTL(在构建期间运行)来生成 ID。但是对于这个特殊的用例,请不要这样做。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-12-13
  • 2018-12-17
  • 1970-01-01
  • 1970-01-01
  • 2011-03-09
  • 2011-07-01
  • 1970-01-01
相关资源
最近更新 更多