我应该创建一个复合组件并将其插入父组件中。我试着在stackOverflow上找到我的问题的答案,我发现了这个:
Add programmatically composite component in backing bean
所以,我创建了复合组件:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:p="http://xmlns.jcp.org/jsf/passthrough"
xmlns:composite="http://java.sun.com/jsf/composite"
xmlns:ic="http://java.sun.com/jsf/composite/inputComponent">
<composite:interface>
<composite:attribute name="idPanel" required="true" />
<composite:attribute name="typeBSPanel" default="default" />
<composite:attribute name="idPanelHeading" required="true" />
<composite:attribute name="idPanelBody" required="true" />
<composite:attribute name="listInputText" required="true"
shortDescription="Questo componente è un Panel con un numero variabile di InputText di tipo Text, Number e DropDown. Questa lista deve contenere degli oggetti con le seguenti proprietà: label, value e placeholder."></composite:attribute>
<composite:attribute name="objectBindToPanel" required="true" />
</composite:interface>
<composite:implementation>
<div class="panel-group" id="accordion">
<div id="#{cc.attrs.idPanel}"
class="panel panel-#{cc.attrs.typeBSPanel}">
<div id="#{cc.attrs.idPanelHeading}" class="panel-heading">
<button type="button" class="btn btn-#{cc.attrs.typeBSPanel}"
data-toggle="collapse" data-target="#collapseBodyPanel">
+/-</button>
</div>
<div id="collapseBodyPanel" class="panel-collapse collapse in">
<div id="#{cc.attrs.idPanelBody}" class="panel-body">
<ui:repeat var="inputText" value="#{cc.attrs.listInputText}">
<ic:inputTextBS preAddon="#{inputText.label}"
requiredMessage="This field must not be empty"
placeholder="#{inputText.placeholder}"
value="#{cc.attrs.objectBindToPanel[inputText.value]}" required="true">
</ic:inputTextBS>
</ui:repeat>
</div>
</div>
</div>
</div>
</composite:implementation>
</html>
用于以动态方式添加复合组件的类和方法:
public class CCUtility {
public static void includeCompositeComponent(UIComponent parent, String libraryName, String resourceName, String id, Map<String, String> mapValueExpression) {
// Prepare.
FacesContext context = FacesContext.getCurrentInstance();
Application application = context.getApplication();
FaceletContext faceletContext = (FaceletContext) context.getAttributes().get("javax.faces.FACELET_CONTEXT");
// This basically creates <ui:component> based on <composite:interface>.
Resource resource = application.getResourceHandler().createResource(resourceName, libraryName);
UIComponent composite = application.createComponent(context, resource);
composite.setId(id); // Mandatory for the case composite is part of UIForm! Otherwise JSF can't find inputs.
ExpressionFactory factory = application.getExpressionFactory();
ELContext ctx = context.getELContext();
for (Map.Entry<String, String> entry : mapValueExpression.entrySet()) {
ValueExpression expr = factory.createValueExpression(ctx, entry.getValue(), String.class);
composite.setValueExpression(entry.getKey(), expr);
//composite.getAttributes().put(entry.getKey(), entry.getValue());
}
// This basically creates <composite:implementation>.
UIComponent implementation = application.createComponent(UIPanel.COMPONENT_TYPE);
implementation.setRendererType("javax.faces.Group");
composite.getFacets().put(UIComponent.COMPOSITE_FACET_NAME, implementation);
// Now include the composite component file in the given parent.
parent.getChildren().add(composite);
parent.pushComponentToEL(context, composite); // This makes #{cc} available.
try {
faceletContext.includeFacelet(implementation, resource.getURL());
} catch (IOException e) {
throw new FacesException(e);
}
}
}
我在另一个类中调用前一个方法,该类传递复合组件所需的属性映射:
public void addPanelHostMachine(){
this.dataCenterController.getDataCenter().getGroupsHost().add(indexGroupHostMachine, new GroupHost());
Map<String, String> mapValueExpression = new HashMap<String, String>();
mapValueExpression.put("idPanel", "panelHostMachine" + indexGroupHostMachine);
mapValueExpression.put("idPanelHeading", "panelHostMachineHeading" + indexGroupHostMachine);
mapValueExpression.put("idPanelBody", "panelHostMachineBody" + indexGroupHostMachine);
mapValueExpression.put("typeBSPanel", "success");
mapValueExpression.put("listInputText", "#{dataCenterController.dataCenter.groupsHost.get(" + indexGroupHostMachine + ").listOfInputText}");
mapValueExpression.put("objectbindToPanel", "#{dataCenterController.dataCenter.groupsHost.get(" + indexGroupHostMachine + ")}");
CCUtility.includeCompositeComponent(panelBodyDataCenter, "panelComponent", "panelComponent.xhtml", "ccPanelHostMachine" + indexGroupHostMachine, mapValueExpression);
indexGroupHostMachine = indexGroupHostMachine + 1;
}
现在的问题是,当我尝试添加CompositeComponent时,我收到此错误:
Grave: Servlet.service() for servlet [Faces Servlet] in context with path [/IcaroKBMassiveEditor] threw exception [/resources/panelComponent/panelComponent.xhtml @34,79 preAddon="#{inputText.label}": Property 'label' not found on type java.lang.String] with root cause
javax.el.PropertyNotFoundException: Property 'label' not found on type java.lang.String
我认为这是EL表达式和'composite:attribute'的问题,但我不知道如何解决这个问题。任何人都可以帮助我吗?
答案 0 :(得分:2)
我在这篇文章中找到了解决方案:DataTable Inside Composite Component。
而不是:
for (Map.Entry<String, String> entry : mapValueExpression.entrySet()) {
ValueExpression expr = factory.createValueExpression(ctx, entry.getValue(),String.class);
composite.setValueExpression(entry.getKey(), expr);
}
我用过:
for (Map.Entry<String, String> entry : mapValueExpression.entrySet()) {
ValueExpression expr = factory.createValueExpression(ctx, entry.getValue(),Object.class);
composite.setValueExpression(entry.getKey(), expr);
}
我在funciton call String.Class
中将Object.class
替换为factory.createValueExpression