ui:repeat 添加在第一个元素中不起作用的新项目

ui:repeat add new item not working in first element

我目前正在尝试使用 adding/removing primefaces p:commandButton.

做一个简单的输入列表

我在 Glassfish 4.1.1Mojarra 2.2.12 上使用 PrimeFaces 6.2 .

ExampleBean.java

package /* myPackage */;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javafx.util.Pair;
import javax.annotation.PostConstruct;
import javax.enterprise.context.SessionScoped;
import javax.inject.Named;

@Named(value = "exampleBean")
@SessionScoped
public class ExampleBean implements Serializable {

    private List<Pair<Integer, Integer>> list;

    @PostConstruct
    public void init() {
        list = new ArrayList<>();
        addNewItem();
    }

    public void addNewItem() {
        list.add(new Pair<>(1, 300));
    }

    public List<Pair<Integer, Integer>> getList() {
        return list;
    }

    public void setList(List<Pair<Integer, Integer>> list) {
        this.list = list;
    }
}

example.xhtml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html" 
      xmlns:ui="http://java.sun.com/jsf/facelets" 
      xmlns:p="http://primefaces.org/ui" 
      xmlns:jsf="http://xmlns.jcp.org/jsf">

    <h:head>
        <title>Example</title>
        <meta charset="utf-8" />
    </h:head>

    <h:body>
        <h:form id="example-form">
            <div jsf:id="example-container">
                <ui:repeat value="#{exampleBean.list}" var="item" varStatus="status">
                    <div>
                        <p:inputText value="#{item.key}" />
                        <p:inputText value="#{item.value}" />

                        <p:commandButton 
                            value="Delete" 
                            actionListener="#{exampleBean.list.remove(item)}" 
                            process="@this" 
                            update="example-form:example-container" 
                            rendered="#{!status.first}" />

                        <p:commandButton 
                            value="Add" 
                            actionListener="#{exampleBean.addNewItem()}" 
                            process="@this" 
                            update="example-form:example-container" 
                            rendered="#{status.first}" />
                    </div>

                </ui:repeat>
            </div>
        </h:form>
    </h:body>
</html>

使用我的每个 p:commandButtonrendered 属性,我只想在第一个项目上显示添加按钮,并在除第一个项目之外的所有项目上显示删除按钮(使其成为无法删除)。

我的问题是,在 添加按钮 上使用 rendered="#{status.first}" 会使整个过程无法正常工作。

<p:commandButton ... rendered="#{status.last}" /> <!-- actionListener called -->

<p:commandButton ... rendered="#{true}" /> <!-- actionListener called -->

<p:commandButton ... rendered="#{status.first}" /> <!-- actionListener NOT called -->

使用rendered="#{status.first}",单击按钮不会调用actionListener,但会触发update

我不知道在第一个项目而不是其他项目或最后一个项目中显示它会发生什么变化。

我认为这与 <ui:repeat> 标志和命令按钮的唯一 ID 的生成有关。视图可能会变得混乱。当您添加其他按钮时,生成的 ID 会发生变化。另外,引入 rendered 属性也会改变组件的处理方式。

所以要解决这个问题,我认为您需要重新处理 ui:repeat 组件和命令按钮以获得有效的操作 URL。解决方案很简单 - 删除 process="@this".

我用可行的解决方案调整了您的示例(该示例使用 Lombok);

@Data @Named @ViewScoped
public class ExampleBean implements Serializable {

    private List<IntegerPair> list;

    @PostConstruct
    private void init() {
        list = new ArrayList<>();
        addNewItem();
    }

    public void addNewItem() {
        list.add(new IntegerPair(1, 300));
    }

    @Data
    @AllArgsConstructor
    public class IntegerPair {
        private int key, value;
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" 
      xmlns:ui="http://java.sun.com/jsf/facelets"  xmlns:p="http://primefaces.org/ui" 
      xmlns:jsf="http://xmlns.jcp.org/jsf">

    <h:head>
        <title>Example</title>
        <meta charset="utf-8" />
    </h:head>

    <h:body>
        Test!
        <h:form id="example-form">
            <div jsf:id="example-container">
                <ui:repeat value="#{exampleBean.list}" var="item" varStatus="status">
                    <div>
                        <p:inputText value="#{item.key}" />
                        <p:inputText value="#{item.value}" />

                        <p:commandButton 
                            value="Delete" 
                            actionListener="#{exampleBean.list.remove(item)}" 
                            update="example-form:example-container" 
                            rendered="#{!status.first}" />

                        <p:commandButton 
                            value="Add" 
                            actionListener="#{exampleBean.addNewItem}" 
                            update="example-form:example-container" 
                            rendered="#{status.first}" />
                    </div>

                </ui:repeat>
            </div>
        </h:form>
    </h:body>
</html>

注意到 IntegerPair class 了吗?我不得不介绍这个,因为 JavaFX 中定义的 Pair<> class 没有可写键 属性 - JSF 在处理输入组件的支持 bean 值时需要可写属性。

坦率地说,由于您面临的问题源自 <ui:repeat> component/tag,因此在您的命令按钮上执行 process="example-form:example-container" 应该也能正常工作 - 只需确保 [=12] =] 包含在处理阶段。