Search code examples
jsf-2managed-beancomposite-component

How do you pass a Managed Bean of different type to a composite for reuse?


I'm creating an inline text editor. I've written the code to edit one single h:ouputText field (h:inputHidden). Works. Thus, I thought I create a composite (widget), that I can call for every field I want to update. Of course, those fields refer to a managed bean in my case, that is the PubController MB.

<composite:interface name="inlineEditor">
    <composite:attribute name="attrOfMb" required="true"
        type="java.lang.String" />
    <composite:attribute name="pubController" required="true" type="com.playground.webapp.controller.PubController"/>
</composite:interface>

Now, I have the following tasks to accomplish:

  1. pass the MB to the composite (done).
  2. in the composite, create the div and the hidden input field with id of the MB attribute.

Challenge where I struggle:

  • How do I "bind" an attribute of the passed PubController to the hidden input field? #{cc.attrs.pubController.title}? Well, it should not always be the same attribute. The attribute should be chosen by what has been passed in the interface attribute attrOfMb.
  • In other cases, its not necessariley PubController. Might be another MB. My initial thought was to define the interface type to javax.faces.bean.ManagedBean. On the other hand, how could you then "bind" the passed MB to the input hidden field (respectively vice-versa).

Is there a JSF-Pattern on how you accomplish these things?


Solution

  • You're going in the wrong path as to designing the composite. You should bind a bean property, not a whole bean.

    I.e. you should not have

    <my:composite bean="#{bean}" />
    

    but you should have

    <my:composite value="#{bean.value}" title="#{bean.title}" />
    

    Once you fix that problem, then you can easily reuse it on any backing bean. Please note that this is also the way how standard JSF <h:xxx> components work. If you worry about about "too many" attributes for some unclear reason, then just create a reusable model class which in turn can be a property of the backing bean.

    <my:composite data="#{bean.data}" />
    

    This way you can use it further in the composite as #{cc.attrs.data.value}, #{cc.attrs.data.title}, etc.

    If you really, really need to bind a whole bean, then I'd question if a tag file or maybe an include file isn't a better solution for whatever functional requirement you've had in mind. A composite component should really represent a component with a single responsibility and a single point of model value binding.

    See also: