Binding a managed bean instance to composite component
Asked Answered
A

1

2

I have a composite component (collapsiblePanel). The component uses the "collapsible" bean to provide the toggle function. When I use the same component multiple times on a page, each instance of the component is bound to the same bean instance. How Can I achieve something like a component scoped bean?

collapsibleTemp.xhtml:

<cc:interface>
    <cc:attribute name="model" required="true">
        <cc:attribute name="collapsed" required="true" />
        <cc:attribute name="toggle" required="true"
            method-signature="java.lang.String f()" />
    </cc:attribute>
    <cc:actionSource name="toggle" />
    <cc:facet name="header" />
    <cc:facet name="body" />
</cc:interface>
<cc:implementation>
    <h:panelGroup layout="block" styleClass="collapsiblePanel-header">
        <h:commandButton id="toggle" action="#{cc.attrs.model.toggle}"
            styleClass="collapsiblePanel-img"
            image="#{cc.attrs.model.collapsed ? '/resources/images/plus.png' : '/resources/images/minus.png'}" />
        <cc:renderFacet name="header" />
    </h:panelGroup>
    <h:panelGroup layout="block" rendered="#{!cc.attrs.model.collapsed}">
        <cc:insertChildren />
        <cc:renderFacet name="body"></cc:renderFacet>
    </h:panelGroup>
    <h:outputStylesheet library="css" name="components.css" />
</cc:implementation>

The backing bean:

@ManagedBean
@ViewScoped
public class Collapsible {

    private boolean collapsed = false;

    public boolean isCollapsed() {
        return collapsed;
    }

    public void setCollapsed(boolean collapsed) {
        this.collapsed = collapsed;
    }

    public String toggle() {
        collapsed = !collapsed;
        return null;
    }

}

Using Page

<h:form id="someid">
    <jl:collapsibletemp id="collapsiblePanel1" model="#{collapsible}">
        <f:facet name="header">
            <h3>
                <h:outputText value="Collapsible information" />
            </h3>
        </f:facet>
        <f:facet name="body">
            <h:outputText value="do something....." />
        </f:facet>
        <p />
    </jl:collapsibletemp>

    <jl:collapsibletemp id="collapsiblePanel2" model="#{collapsible}">
        <f:facet name="header">
            <h3>
                <h:outputText value="Collapsible information" />
            </h3>
        </f:facet>
        <f:facet name="body">
            <h:outputText value="do some tabbing" />
        </f:facet>
        <p />
    </jl:collapsibletemp>

    <jl:collapsibletemp id="collapsiblePanel3" model="#{collapsible}">
        <f:facet name="header">
            <h3>
                <h:outputText value="Collapsible information" />
            </h3>
        </f:facet>
        <f:facet name="body">
            <h:outputText value="notice board" />
        </f:facet>
        <p />
    </jl:collapsibletemp>
</h:form>
Anatolic answered 6/7, 2011 at 21:4 Comment(0)
S
4

You can use the componentType attribute of the <cc:interface> to define a "backing component".

E.g.

<cc:interface componentType="collapsiblePanel">
    ...
</cc:interface>
<cc:implementation>
    ...
    <h:commandButton action="#{cc.toggle}" ... />
    ...
    <h:panelGroup rendered="#{!cc.collapsed}" ...>
    ...
</cc:implementation>

with just a com.example.components.CollapsiblePanel

@FacesComponent(value="collapsiblePanel") // To be specified in componentType attribute.
public class CollapsiblePanel extends UINamingContainer { // Important! Must extend UINamingContainer.

    private boolean collapsed;

    public void toggle() {
        collapsed = !collapsed;
    }

    public boolean isCollapsed() {
        collapsed;
    }

}

However, when you want to have multiple of those components, then you should declare physically separate instances of them in the view. If this needs to happen dynamically, then you need to use <c:forEach> to generate physically separate instances of them instead of <ui:repeat> with a single component. Otherwise you have to map all collapsed states by the client ID inside a Map<String, Boolean>. See for an example and more background information also Getting same instance of `componentType` in composite component on every use

Schinica answered 6/7, 2011 at 21:59 Comment(5)
This solution works partially. When I toggle one component, the other component also gets toggled. I have manually declared the components 3 times. When I toggle on any one component, the other 2 also toggle. <jl:collapsibletemp id="collapsiblePanel1"></jl:collapsibletemp> <jl:collapsibletemp id="collapsiblePanel2"></jl:collapsibletemp> <jl:collapsibletemp id="collapsiblePanel3"></jl:collapsibletemp> Thanks for your help. Please let me know what I could do to make these components completely independent of each other.Anatolic
This is exactly what I already predicted and covered in the last paragraph of my answer. Use <c:forEach> or hold the booleans in a map with client ID as key.Schinica
I made the changes as you suggested. But still it does not seem to work. Using Page: <js:forEach items="#{widgets.windows}" var="window"> <jl:collapsibletemp id="collapsiblePanel#{window.name}" /> </js:forEach> @ManagedBean(name="widgets") public class Widgets { public List<Window> getWindows() { if(windows == null){ windows = new ArrayList<Window>(); windows.add(new Window("one", "one header", "one body")); windows.add(new Window("two", "two header", "two body")); } return windows; Even after adding the collapsed states into a map, they do not behave like independent components.Anatolic
@FacesComponent(value = "collPanelTwo") class CollPanel extends UICompBase implements NContainer { public void toggle(ActionEvent evt) { collapsed = !collapsed; testMap.put(getClientId(), isCollapsed()); } public boolean isCollapsed() { return testMap.get(getClientId()); } USING PAGE <jl:collapsibleComponent clientId="one">facets... <jl:collapsibleComponent clientId="two">facets... I have manually added 2 components in the views. They do not behave like independent components. I also tried <jstl:forEach but it never rendered as an independent componentAnatolic
Thanks BalucS. I had to work around a bit to get it to work. I used the stateHelper to preserve the state.Anatolic

© 2022 - 2024 — McMap. All rights reserved.