0

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?

Community
  • 1
  • 1
Claudio Badii
  • 53
  • 1
  • 8
  • Did you solve it? Which is your `#{cc.attrs.listInputText}` type? – Aritz Mar 08 '14 at 13:07
  • Yes, I did. I used the solution that it's explain in the link posted in my message edit. – Claudio Badii Mar 10 '14 at 08:29
  • As you're new here, just notice you can answer your own question too. In fact, that's the way to go. If you post a question and you find a solution later on, post the answer yourself (in the answer part, below the question). Otherwise, question will be unresolved and it's preferrable to remove it rather than leaving it without the solution. – Aritz Mar 10 '14 at 08:31
  • I'm sorry, it's ok now? I edited my question with the answer, I didn't know that I had to answer my question with a new post. – Claudio Badii Mar 10 '14 at 08:40
  • That's good mate ;-) Now you only need to mark it as the accepted answer (click on the checkmark ✓) when Stack overflow allows you. – Aritz Mar 10 '14 at 08:42
  • 1
    Ok, thank you. At the next! :-) – Claudio Badii Mar 10 '14 at 08:43

1 Answers1

2

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

Community
  • 1
  • 1
Claudio Badii
  • 53
  • 1
  • 8