【问题标题】:Primefaces selectBooleanCheckbox value lost on ajax actionPrimefaces selectBooleanCheckbox 值在 ajax 操作中丢失
【发布时间】:2017-05-23 13:41:13
【问题描述】:

我在这里发布 MCVE 是因为我对 JSF 的行为感到困惑。 我有 index.xhtml:

<h:form id="entityForm" enctype="multipart/form-data" styleClass="entityForm">
<p:dataTable id="dataTableId1" value="#{testBean.testEntityList}" var="tent" rowKey="#{tent.id}">
    <p:column style="width:16px">
        <p:rowToggler/>
    </p:column>
    <p:column>
        <h:outputText value="#{tent.id}"/>
    </p:column>
    <p:column>
        <h:outputText value="#{tent.name}"/>
    </p:column>
    <p:rowExpansion>
        <p:selectBooleanCheckbox value="#{tent.something}"/>
        <p:inputText value="#{tent.strNextToSomethingCheckBox}" />
    </p:rowExpansion>
</p:dataTable>
<p:commandButton id="button1" ajax="true" value="Do something" update="entityForm"/>
</h:form>

豆码:

@ManagedBean(name = "testBean")
@ViewScoped
public class TestBean implements Serializable {

private static final Logger LOG = Logger.getLogger(TestBean.class);

private List<TestEntity> testEntityList;

@PostConstruct
public void init() {
    LOG.debug("init call");
    testEntityList = new ArrayList<>();

    testEntityList.add(new TestEntity(1L, "Entry 1", true, "Value 1 str"));
    testEntityList.add(new TestEntity(2L, "Entry 2", false, "Value 2 str"));
    testEntityList.add(new TestEntity(3L, "Entry 3", false, "Value 3 str"));

}

public class TestEntity {

    Long id;

    String name;

    Boolean something;

    String strNextToSomethingCheckBox;


    public TestEntity(Long id, String name, Boolean something, String strNextToSomethingCheckBox) {
        this.id = id;
        this.name = name;
        this.something = something;
        this.strNextToSomethingCheckBox = strNextToSomethingCheckBox;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Boolean getSomething() {
        return something;
    }

    public void setSomething(Boolean something) {
        LOG.debug("setSomething call");
        this.something = something;
    }

    public String getStrNextToSomethingCheckBox() {
        return strNextToSomethingCheckBox;
    }

    public void setStrNextToSomethingCheckBox(String strNextToSomethingCheckBox) {
        LOG.debug("setStrNextToSomethingCheckBox call");
        this.strNextToSomethingCheckBox = strNextToSomethingCheckBox;
    }
}


public List<TestEntity> getTestEntityList() {
    return testEntityList;
}

public void setTestEntityList(List<TestEntity> testEntityList) {
    this.testEntityList = testEntityList;
}

}

场景:当你打开页面时,展开第一行,你会看到复选框状态为选中,当你按下按钮时,表单被更新并且复选框仍然被选中(你需要再次展开行才能看到)

问题场景:打开页面,不展开任何行,点击按钮,表单更新后,展开第一行,你会看到值从true设置为false

问题:为什么值是从真到假?这是错误还是预期的行为?我怎样才能避免这种情况? 谢谢。

【问题讨论】:

  • 如果你从h:form中删除这个:enctype="multipart/form-data"呢?
  • @Kukeltje 相同,值丢失/重置为 false

标签: ajax primefaces jsf-2


【解决方案1】:

您可以参考以下primefacesSelectBooleanCheckboxRenderer#decode代码:

@Override
public void decode(FacesContext context, UIComponent component) {
    SelectBooleanCheckbox checkbox = (SelectBooleanCheckbox) component;

    if(checkbox.isDisabled()) {
        return;
    }

    decodeBehaviors(context, checkbox);

    String clientId = checkbox.getClientId(context);
    String submittedValue = (String) context.getExternalContext().getRequestParameterMap().get(clientId + "_input");

    if(submittedValue != null && isChecked(submittedValue)) {
        checkbox.setSubmittedValue(true);
    }
    else {
        checkbox.setSubmittedValue(false);
    }
}

之所以将值从true设置为false,是因为p:rowExpansion关闭时RequestParameterMap中没有设置p:selectBooleanCheckbox。因此,当p:selectBooleanCheckbox 被解码时,submittedValue 变为空。因此,执行checkbox.setSubmittedValue(false)

p:rowExpansion 展开时,您可以看到p:selectBooleanCheckbox 的值在RequestParameterMap 中。 您可以通过在 bean 中使用以下方法将 action="#{testBean.doSomething}" 添加到 p:commandButton 来确认这一点:

public void doSomething() {
    FacesContext context = FacesContext.getCurrentInstance();
    Map<String, String> requestParameterMap = context.getExternalContext().getRequestParameterMap();
    Set<String> keySet = requestParameterMap.keySet();

    for (String key : keySet) {
        LOG.debug("key>>" + key);
        LOG.debug("value>>" + requestParameterMap.get(key));
    }
}

请注意,p:selectBooleanCheckbox 在未选中时也NOTRequestParameterMap 中设置。

您可以通过覆盖 SelectBooleanCheckboxRenderer#decode 并更改以下内容来避免此问题:

if(submittedValue != null && isChecked(submittedValue)) {
    checkbox.setSubmittedValue(true);
}
else {
    checkbox.setSubmittedValue(false);
}

到这里:

if (submittedValue != null && isChecked(submittedValue)) {
    checkbox.setSubmittedValue(true);
} else {
    if (checkbox.isRendered()) {
        checkbox.setSubmittedValue(false);
    }
}

这样,只有当p:selectBooleanCheckbox未被选中并被渲染时,该值才会为假。

编辑:

对不起。我的覆盖解决方案不正确但你的想法是你需要覆盖SelectBooleanCheckboxRenderer#decode

我会在可用时发布正确的代码。

编辑 2:

似乎无法通过覆盖 SelectBooleanCheckboxRenderer#decode 使其工作,所以我尝试了不同的方法。

我在 TestEntity 中添加了一个标志(切换),其初始值为 false。 这将用作在p:rowExpansion 内呈现p:selectBooleanCheckbox 的标志。
当使用 bean 中的 p:ajax event="rowToggle"onRowToggle 方法切换(可见)行时,此标志将设置为 true。

这个想法是当p:selectBooleanCheckbox没有被渲染时SelectBooleanCheckboxRenderer#decode不会被执行。 这将导致值被保留。这种方法需要覆盖SelectBooleanCheckboxRenderer#decode

代码如下:

xhtml:

<h:form id="entityForm" enctype="multipart/form-data" styleClass="entityForm">
<p:dataTable id="dataTableId1" value="#{testBean.testEntityList}" var="tent" rowKey="#{tent.id}">

    <p:ajax event="rowToggle" listener="#{testBean.onRowToggle}"/>

    <p:column style="width:16px">
        <p:rowToggler/>
    </p:column>
    <p:column>
        <h:outputText value="#{tent.id}"/>
    </p:column>
    <p:column>
        <h:outputText value="#{tent.name}"/>
    </p:column>
    <p:rowExpansion>
        <p:selectBooleanCheckbox value="#{tent.something}" rendered="#{tent.toggled}"/>
        <p:inputText value="#{tent.strNextToSomethingCheckBox}" />
    </p:rowExpansion>
</p:dataTable>
<p:commandButton id="button1" ajax="true" value="Do something" update="entityForm"/>
</h:form>

豆:

@ManagedBean(name = "testBean")
@ViewScoped
public class TestBean implements Serializable {

    private static final Logger LOG = Logger.getLogger(TestBean.class);

    private List<TestEntity> testEntityList;

    @PostConstruct
    public void init() {
        LOG.debug("init call");
        testEntityList = new ArrayList<>();

        testEntityList.add(new TestEntity(1L, "Entry 1", true, "Value 1 str", false));
        testEntityList.add(new TestEntity(2L, "Entry 2", false, "Value 2 str", false));
        testEntityList.add(new TestEntity(3L, "Entry 3", false, "Value 3 str", false));
    }

    public class TestEntity {

        Long id;

        String name;

        Boolean something;

        String strNextToSomethingCheckBox;

        boolean toggled;

        public TestEntity(Long id, String name, Boolean something, String strNextToSomethingCheckBox, boolean toggled) {
            this.id = id;
            this.name = name;
            this.something = something;
            this.strNextToSomethingCheckBox = strNextToSomethingCheckBox;
            this.toggled = toggled;
        }

        // getters and setters
    }

    public List<TestEntity> getTestEntityList() {
        return testEntityList;
    }

    public void setTestEntityList(List<TestEntity> testEntityList) {
        this.testEntityList = testEntityList;
    }

    public void onRowToggle(ToggleEvent event) {
        TestEntity entity = (TestEntity) event.getData();

        if (event.getVisibility() == Visibility.VISIBLE) {
            entity.setToggled(true);
        } else {
            entity.setToggled(false);
        }
    }
}

希望这会有所帮助。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-11-27
    • 1970-01-01
    • 1970-01-01
    • 2013-01-17
    • 2013-02-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多