我正在使用JSF 2.2 / Mojarra 2.2.8
在我的模型中有java.util.Set
,我想编辑Set
public class MyModel {
private Set<Foo> fooSet;
private Set<Bar> barSet;
// getters and setters
}
public class Foo {
private String label;
//getter and setter
}
public class Bar {
private String name;
// getter and setter
}
我正在使用复合组件
<h:form>
<ez:editFooSet myModel="#{someBean.myModel}"/>
<ez:editBarSet myModel="#{someBean.myModel}"/>
<!-- ... -->
</h:form>
我的想法是将ui:repeat
所需的List存储在JSF ManagedBean中,并使用@FacesComponent
将Set
转换为List
encodeBegin()
和List
Set
到updateModel()
editFooSet.xhtml:
<cc:interface componentType="my.app.component.FooSetComponent">
<cc:attribute name="myModel" type="my.app.model.MyModel" required="true"/>
</cc:interface>
<cc:implementation>
<ui:repeat value="#{fooSetBean.value}" var="item">
<h:outputLabel value="Foo label: "/>
<h:inputText value="#{item.label}"/>
<h:commandButton value="remove" action="#{fooSetBean.remove(item)}"/>
</ui:repeat>
<h:commandButton value="add" action="#{fooSetBean.add()}"/>
</cc:implementation>
FooSetBean.java
@Named
@ViewScoped
public class FooSetBean {
private List<Foo> value;
// getter and setter
puvlic void remove(Foo foo) {
fooList.remove(foo);
}
public void add() {
fooList.add(new Foo());
}
}
和FooSetComponent.java:
@FacesComponent("my.app.component.FooSetComponent")
public class FooSetComponent extends UIInput implements NamingContainer {
@Override
public String getFamily() {
return UINamingContainer.COMPONENT_FAMILY;
}
@Override
public Object getSubmittedValue() {
return null;
}
@Override
public void encodeBegin(FacesContext context) throws IOException {
MyModel model = (MyModel) super.getAttributes().get("myModel");
Collection<Foo> foos = model.getFooSet();
List<Foo> fooList = new ArrayList<>(foos);
FooSetBean bean = context.getApplication().evaluateExpressionGet(context, "#{fooSetBean}", FooSetBean.class) ;
bean.setValue(fooList);
super.encodeBegin(context);
}
@Override
public void updateModel(FacesContext context) {
MyModel model = (MyModel) super.getAttributes().get("myModel");
FooSetBean bean = context.getApplication().evaluateExpressionGet(context, "#{fooSetBean}", FooSetBean.class) ;
Collection<Foo> newValue = bean.getValue();
model.setFooSet(new HashSet<>(newValue));
}
}
和editBarSet.xhtml,BarSetBean.java和BarSetComponent.java相同
该解决方案正在运作
我的问题是我有很多Set
而且我想分解这段代码
我希望有类似的东西:
<h:form>
<ez:editRepeat value="#{someBean.myModel.fooSet}" itemClass="#{Foo.class}">
<h:outputLabel value="Foo label: "/>
<h:inputText value="#{item.label"/>
</ez:editRepeat>
<ez:editRepeat value="#{someBean.myModel.barSet}" itemClass="#{Bar.class}">
<h:outputLabel value="Bar name: "/>
<h:inputText value="#{item.name}"/>
</ez:editRepeat>
<!-- ... -->
</h:form>
使用editRepeat.xhtml:
<cc:interface componentType="my.app.component.EditRepeatComponent">
<cc:attribute name="value" type="java.util.collection" required="true"/>
<cc:attribute name="itemClass" type="java.lang.Class" required="true"/>
</cc:interface>
<cc:implementation>
<ui:repeat value="#{fooSetBean.value}" var="item" id="repeat">
<cc:insertChildren/>
<h:commandButton value="remove" action="#{cc.remove(item)}"/>
</ui:repeat>
<h:commandButton value="add" action="#{cc.add()}"/>
</cc:implementation>
使用EditRepeatComponent.java
@FacesComponent("my.app.component.EditRepeatComponent")
public class EditRepeatComponent extends UIInput implements NamingContainer {
@Override
public String getFamily() {
return UINamingContainer.COMPONENT_FAMILY;
}
@Override
public void encodeBegin(FacesContext context) throws IOException {
Collection value = (Collection) super.getAttributes().get("value");
List<Foo> list = new ArrayList<>(value);
setList(list);
super.encodeBegin(context);
}
public List getList() {
return (List) getStateHelper().get("list");
}
public void setList(List list) {
getStateHelper().put("list", list);
}
public void add() {
try {
Class itemClass = (Class) super.getAttributes().get("itemClass");
Object newItem = itemClass.newInstance();
getList().add(newItem);
} catch (InstantiationException | IllegalAccessException ex) {
throw new RuntimeException(ex);
}
}
public void remove(Object item) {
getList().remove(item);
}
@Override
public void updateModel(FacesContext context) {
// ???
}
@Override
public Object getSubmittedValue() {
// ???
}
}
但这并不奏效 几秒钟后(系统在1秒内工作)我有一个例外:
java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
at java.util.ArrayList.rangeCheck(ArrayList.java:653)
at java.util.ArrayList.get(ArrayList.java:429)
at javax.faces.component.AttachedObjectListHolder.restoreState(AttachedObjectListHolder.java:166)
at javax.faces.component.UIComponentBase.restoreState(UIComponentBase.java:1611)
at com.sun.faces.application.view.FaceletPartialStateManagementStrategy$2.visit(FaceletPartialStateManagementStrategy.java:380)
at com.sun.faces.component.visit.FullVisitContext.invokeVisitCallback(FullVisitContext.java:151)
at javax.faces.component.UIComponent.visitTree(UIComponent.java:1689)
at javax.faces.component.UIComponent.visitTree(UIComponent.java:1700)
at javax.faces.component.UIComponent.visitTree(UIComponent.java:1700)
at com.sun.faces.application.view.FaceletPartialStateManagementStrategy.restoreView(FaceletPartialStateManagementStrategy.java:367)
at com.sun.faces.application.StateManagerImpl.restoreView(StateManagerImpl.java:138)
at com.sun.faces.application.view.ViewHandlingStrategy.restoreView(ViewHandlingStrategy.java:123)
at com.sun.faces.application.view.FaceletViewHandlingStrategy.restoreView(FaceletViewHandlingStrategy.java:585)
at com.sun.faces.application.view.MultiViewHandler.restoreView(MultiViewHandler.java:150)
at javax.faces.application.ViewHandlerWrapper.restoreView(ViewHandlerWrapper.java:353)
at javax.faces.application.ViewHandlerWrapper.restoreView(ViewHandlerWrapper.java:353)
at javax.faces.application.ViewHandlerWrapper.restoreView(ViewHandlerWrapper.java:353)
at org.omnifaces.viewhandler.RestorableViewHandler.restoreView(RestorableViewHandler.java:86)
at com.sun.faces.lifecycle.RestoreViewPhase.execute(RestoreViewPhase.java:197)
at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
at com.sun.faces.lifecycle.RestoreViewPhase.doPhase(RestoreViewPhase.java:121)
at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:198)
at javax.faces.webapp.FacesServlet.service(FacesServlet.java:646)
at io.undertow.servlet.handlers.ServletHandler.handleRequest(ServletHandler.java:85)
at io.undertow.servlet.handlers.security.ServletSecurityRoleHandler.handleRequest(ServletSecurityRoleHandler.java:61)
at io.undertow.servlet.handlers.ServletDispatchingHandler.handleRequest(ServletDispatchingHandler.java:36)
at org.wildfly.extension.undertow.security.SecurityContextAssociationHandler.handleRequest(SecurityContextAssociationHandler.java:78)
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at io.undertow.servlet.handlers.security.SSLInformationAssociationHandler.handleRequest(SSLInformationAssociationHandler.java:131)
at io.undertow.servlet.handlers.security.ServletAuthenticationCallHandler.handleRequest(ServletAuthenticationCallHandler.java:56)
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at io.undertow.security.handlers.AbstractConfidentialityHandler.handleRequest(AbstractConfidentialityHandler.java:45)
at io.undertow.servlet.handlers.security.ServletConfidentialityConstraintHandler.handleRequest(ServletConfidentialityConstraintHandler.java:63)
at io.undertow.security.handlers.AuthenticationMechanismsHandler.handleRequest(AuthenticationMechanismsHandler.java:58)
at io.undertow.servlet.handlers.security.CachedAuthenticatedSessionHandler.handleRequest(CachedAuthenticatedSessionHandler.java:70)
at io.undertow.security.handlers.SecurityInitialHandler.handleRequest(SecurityInitialHandler.java:76)
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at org.wildfly.extension.undertow.security.jacc.JACCContextIdHandler.handleRequest(JACCContextIdHandler.java:61)
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at io.undertow.servlet.handlers.ServletInitialHandler.handleFirstRequest(ServletInitialHandler.java:261)
at io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:247)
at io.undertow.servlet.handlers.ServletInitialHandler.access$000(ServletInitialHandler.java:76)
at io.undertow.servlet.handlers.ServletInitialHandler$1.handleRequest(ServletInitialHandler.java:166)
at io.undertow.server.Connectors.executeRootHandler(Connectors.java:197)
at io.undertow.server.HttpServerExchange$1.run(HttpServerExchange.java:759)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
我不明白为什么
我还不知道如何实施updateModel()
或getSubmittedValue()
以使所有系统正常工作
答案 0 :(得分:1)
2天后我终于成功了
Omnifaces doc http://showcase.omnifaces.org/functions/Converters给了我处理ui的解决方案:repeat:using toArray()
<h:form>
<ez:editRepeat value="#{someBean.myModel.fooSet}" itemClass="my.app.model.Foo">
<h:outputLabel value="Foo label: "/>
<h:inputText value="#{item.label}"/>
</ez:editRepeat>
<ez:editRepeat value="#{someBean.myModel.barSet}" itemClass="my.app.model.Bar">
<h:outputLabel value="Bar name: "/>
<h:inputText value="#{item.name}"/>
</ez:editRepeat>
<!-- ... -->
</h:form>
editRepeat.xtml(我使用primeface p:commanButton
来指定update
和process
属性,这样我就不会失去未提及的输入而且我不会#39 ; t提交所有表格)
<cc:interface>
<cc:attribute name="value" type="java.util.Collection" required="true"/>
<cc:attribute name="itemClass" type="java.lang.String" required="true"/>
</cc:interface>
<cc:implementation>
<h:panelGroup style="display: block; background-color: rgba(200, 200, 200, 0.5); padding: 12px;">
<ui:repeat value="#{cc.attrs.value.toArray()}" var="item">
<h:panelGroup style="background-color: rgba(200, 200, 200, 0.5); margin-left: 12px; margin-bottom: 12px; display: block; padding: 12px;">
<cc:insertChildren/>
<p:commandButton value="remove" action="#{editRepeatBean.remove(cc.attrs.value, item)}"
update="@parent:@parent:@parent" process="@parent:@parent:@parent"
style="margin-left: 12px;"/>
</h:panelGroup>
</ui:repeat>
<p:commandButton value="add" action="#{editRepeatBean.add(cc.attrs.value, cc.attrs.itemClass)}" update="@parent" process="@parent"/>
</h:panelGroup>
</cc:implementation>
EditRepeatBean.java
@Named
@RequestScoped
public class EditRepeatBean {
public void add(Collection collection, String itemClassName) {
try {
Class itemClass = Class.forName(itemClassName);
Object item = itemClass.newInstance();
collection.add(item);
} catch (InstantiationException | IllegalAccessException | ClassNotFoundException ex) {
throw new RuntimeException(ex);
}
}
public void remove(Collection collection, Object item) {
collection.remove(item);
}
}
如果你有:
public class MyModel {
private Set<Foo> fooSet;
// getter and setter
}
public class Foo {
private String label;
private Set<Bar> barSet;
// getters and setters
}
public class Bar {
private String name;
// getter and setter
}
你可以做到
<h:form>
<ez:editRepeat value="#{someBean.myModel.fooSet}" itemClass="my.app.model.Foo">
<h:outputLabel value="Foo label: "/>
<h:inputText value="#{item.label}"/>
<ez:editRepeat value="#{item.barSet}" itemClass="my.app.model.Bar">
<h:outputLabel value="Bar name: "/>
<h:inputText value="#{item.name}"/>
</ez:editRepeat>
</ez:editRepeat>
<!-- ... -->
</h:form>
它也在工作
还有一个问题:Set
不能为空,如果找到解决方案我会编辑
编辑:null
Collection
的解决方案
只需更改editRepeat.xhtml界面即可添加componentType
,以便在encodeBegin()
方法中初始化该集合,并添加cc:attribute
以隐藏Collection
的实现默认值为HashSet
<cc:interface componentType="my.app.component.EditRepeatComponent">
<cc:attribute name="value" type="java.util.Collection" required="true"/>
<cc:attribute name="itemClass" type="java.lang.String" required="true"/>
<cc:attribute name="collectionImpl" type="java.lang.String" default="java.util.HashSet"/>
</cc:interface>
和EditRepeatComponent.java
@FacesComponent("my.app.component.EditRepeatComponent")
public class EditRepeatComponent extends UIInput implements NamingContainer {
@Override
public String getFamily() {
return UINamingContainer.COMPONENT_FAMILY;
}
@Override
public void encodeBegin(FacesContext context) throws IOException {
ELContext elContext = context.getELContext();
ValueExpression valueExpression = super.getValueExpression("value");
if (valueExpression.getValue(elContext) == null) {
try {
String collectionImpl = (String) super.getAttributes().get("collectionImpl");
Class<? extends Collection> collectionClass = (Class<? extends Collection>) Class.forName(collectionImpl);
Collection collection = collectionClass.newInstance();
valueExpression.setValue(elContext, collection);
} catch (InstantiationException | IllegalAccessException | ClassNotFoundException ex) {
throw new RuntimeException(ex);
}
}
super.encodeBegin(context);
}
}
那么仍有问题...当ez:editRepeat
位于其他ez:editRepeat
时,内部ez:editRepeat
的删除无法正常工作
Caused by: javax.el.PropertyNotFoundException: The class 'my.app.model.Bar' does not have the property 'barSet'
编辑:最终解决方案。
使用上一个解决方案,嵌套<editRepeat>
存在问题,在processValidators()
阶段,内部<repeat>
组件的var为空,导致Exception
和我不知道为什么,它可能是一个错误......
解决方案是@Override
processValidators()
并重新设置repeat.var
。
这是完整的解决方案,有一些改进:
EditRepeatBean
的所有代码都已移至EditRepeatComponent
以编程方式更新/渲染和处理/执行
<h:form>
<ez:editRepeat value="#{someBean.myModel.fooSet}"
itemType="my.app.model.Foo"
var="foo">
<h:outputLabel value="Foo label: "/>
<h:inputText value="#{foo.label}"/>
<ez:editRepeat value="#{foo.barSet}"
itemType="my.app.model.Bar"
var="bar">
<h:outputLabel value="Bar name: "/>
<h:inputText value="#{bar.name}"/>
</ez:editRepeat>
</ez:editRepeat>
<!-- ... -->
</h:form>
editRepeat.xhtml(包装器):
<cc:interface>
<cc:attribute name="value" type="java.util.Collection" required="true"/>
<cc:attribute name="itemType" type="java.lang.String" required="true"/>
<cc:attribute name="collectionType" type="java.lang.String" default="java.util.HashSet"/>
<cc:attribute name="var" type="java.lang.String" required="true"/>
</cc:interface>
<cc:implementation>
<h:panelGroup id="#{cc.id}Wrapper">
<ez:editRepeatWrapped value="#{cc.attrs.value}" var="#{cc.attrs.var}"
itemType="#{cc.attrs.itemType}"
collectionType="#{cc.attrs.collectionType}"
id="#{cc.id}Wrapped">
<cc:insertChildren/>
</ez:editRepeatWrapped>
</h:panelGroup>
</cc:implementation>
editRepeatWrapped.xhtml:
<cc:interface componentType="my.app.component.EditRepeatComponent">
<cc:attribute name="value" type="java.util.Collection" required="true"/>
<cc:attribute name="itemType" type="java.lang.String" required="true"/>
<cc:attribute name="collectionType" type="java.lang.String" default="java.util.HashSet"/>
<cc:attribute name="var" type="java.lang.String" required="true"/>
</cc:interface>
<cc:implementation>
<h:panelGroup id="itemsGroup" style="display: block; background-color: rgba(0, 255, 0, 0.20); padding: 6px; margin: 6px;">
<ui:repeat value="#{cc.attrs.value.toArray()}" var="#{cc.attrs.var}"
id="#{cc.attrs.id}Repeat">
<h:panelGroup id="itemGroup" style="background-color: rgba(0, 255, 0, 0.2); margin-left: 12px; margin: 6px; display: block; padding: 6px;">
<cc:insertChildren/>
<p:commandButton value="remove" action="#{cc.remove()}"
style="margin-left: 12px;"/>
</h:panelGroup>
</ui:repeat>
<p:commandButton value="add" action="#{cc.add()}"/>
</h:panelGroup>
</cc:implementation>
EditeRepeatComponent.java:
@FacesComponent("my.app.component.EditRepeatComponent")
public class EditRepeatComponent extends UIInput implements NamingContainer {
@Override
public String getFamily() {
return UINamingContainer.COMPONENT_FAMILY;
}
@Override
public void processValidators(FacesContext context) {
initVar(); // because repeat.var is null at this stage
super.processValidators(context);
}
@Override
public void encodeBegin(FacesContext context) throws IOException {
initValue(context);
initVar();
super.encodeBegin(context);
}
/**
* set var of the repeat component
*/
private void initVar() {
String idRepeatComponent = ((String) super.getAttributes().get("id")) + "Repeat";
String var = (String) getAttributes().get("var");
UIRepeat repeatConponent = (UIRepeat) super.findComponent(idRepeatComponent);
repeatConponent.setVar(var);
}
/**
* if the value is null then initialize the collection with the collection type attribute
*/
private void initValue(FacesContext context) {
ELContext elContext = context.getELContext();
ValueExpression valueExpression = super.getValueExpression("value");
Collection collection = (Collection) valueExpression.getValue(elContext);
if (collection == null) {
try {
String collectionType = (String) getAttributes().get("collectionType");
Class<? extends Collection> collectionClass = (Class<? extends Collection>) Class.forName(collectionType);
collection = collectionClass.newInstance();
valueExpression.setValue(elContext, collection);
} catch (InstantiationException | IllegalAccessException | ClassNotFoundException ex) {
throw new RuntimeException(ex);
}
}
}
public void remove() {
String var = (String) getAttributes().get("var");
Object item = evaluate(var);
Collection collection = (Collection) getAttributes().get("value");
collection.remove(item);
updateView();
}
private Object evaluate(String var) {
FacesContext facesContext = getFacesContext();
ELContext elContext = facesContext.getELContext();
Application application = facesContext.getApplication();
ExpressionFactory expressionFactory = application.getExpressionFactory();
ValueExpression expression = expressionFactory.createValueExpression(elContext, "#{" + var + "}", Object.class);
Object item = expression.getValue(elContext);
return item;
}
public void add() {
try {
Collection collection = (Collection) getAttributes().get("value");
String itemType = (String) getAttributes().get("itemType");
Class itemClass = Class.forName(itemType);
Object item = itemClass.newInstance();
collection.add(item);
updateView();
} catch (InstantiationException | IllegalAccessException | ClassNotFoundException ex) {
throw new RuntimeException(ex);
}
}
/**
* render/update and execute/process the wrapper of the component
*/
private void updateView() {
PartialViewContext context = getFacesContext().getPartialViewContext();
String parentId = this.getParent().getClientId();
context.getRenderIds().add(parentId);
context.getExecuteIds().add(parentId);
}
}
不是<ui:repeat ... var="#{cc.attrs.var}" ...>
没用,var不是这样设置的(我不知道为什么......),它在EditRepeatComponent.initVar()
期间都设置了encodeBegin()
和processValidators()
我只是将var="#{cc.attrs.var}"
用于理解