我在这里发布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>
bean代码:
@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
问题:为什么值设置为true为false?这个错误或预期的行为?我怎么能避免这个? 感谢。
答案 0 :(得分:0)
您可以参考下面的主要SelectBooleanCheckboxRenderer#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关闭时,p:selectBooleanCheckbox
中未设置RequestParameterMap
。因此,当p:selectBooleanCheckbox
被解码时,submittedValue
变为空。因此,执行checkbox.setSubmittedValue(false)
。
展开p:rowExpansion
后,您可以看到p:selectBooleanCheckbox
的值在RequestParameterMap
中。
您可以使用bean中的follow方法将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
在取消选中RequestParameterMap
时也会设置为非。
你可以通过覆盖 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
并进行渲染时,该值才为false。
修改强>
对不起。我的覆盖解决方案不正确但想法是你需要覆盖。SelectBooleanCheckboxRenderer#decode
我会在可用时发布正确的代码。
编辑2:
似乎无法覆盖SelectBooleanCheckboxRenderer#decode
,所以我尝试了不同的方法。
我在TestEntity中添加了一个标志(切换),初始值为false。
这将用作在p:selectBooleanCheckbox
内呈现p:rowExpansion
的标记
当使用bean中的p:ajax event="rowToggle"
和onRowToggle
方法切换(VISIBLE)行时,此标志将设置为true。
我们的想法是,SelectBooleanCheckboxRenderer#decode
在未呈现p:selectBooleanCheckbox
时不会被执行。
这将导致保留该值。这种方法 NOT 需要覆盖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);
}
}
}
希望这有帮助。