Question

I should create a composite component and insert it in a parent component. I tried to find the answer to my problem on stackOverflow and i found this:

Add programmatically composite component in backing bean

So, I create the composite component:

    <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>

the class and the method to add the composite component dinamically:

    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);
        }
    }


}

and I call the previous method in another class that pass the map of Attributes I want for the composite component:

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;
}

Now the problem is that when I try to add the CompositeComponent I get this error:

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

I think that it's a problem with EL expression and 'composite:attribute' but I don't know how to fix this. Can anyone help me?

Was it helpful?

Solution

I find the solution in this post: DataTable Inside Composite Component.

Instead of:

for (Map.Entry<String, String> entry : mapValueExpression.entrySet()) { 
 ValueExpression expr = factory.createValueExpression(ctx, entry.getValue(),String.class); 
  composite.setValueExpression(entry.getKey(), expr); 
        }

I used:

for (Map.Entry<String, String> entry : mapValueExpression.entrySet()) { 
 ValueExpression expr = factory.createValueExpression(ctx, entry.getValue(),Object.class); 
  composite.setValueExpression(entry.getKey(), expr); 
        }

I replaced String.Class to Object.class in funciton call factory.createValueExpression

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top