I want to pass a non-managed (non-String) object as an attribute on a dynamically added composite component, and have it survive the session.
The JSF 2.2 ViewDeclarationLanguage#createComponent
handles dynamic non-string attribute values to composite components differently than the older Mojarra dependent code (Application#createComponent
). I can't find the approach that works completely with the JSF 2.2 technique, but it's probably me.
[I'm trying to remove Mojarra dependencies by converting to MyFaces (and also working around some other Mojarra issues). I'm using JSF 2.2, CDI Weld 2.2.16, Tomcat v8.0]
I'm instantiating different composite components like these programmatically (notice the bean attribute):
<cc:interface>
<cc:attribute name="bean" required="true" type="com.aadhoc.cvc.spikes.extensionsapproach.ExtensionBeanInterface"/>
</cc:interface>
<cc:implementation>
<h:panelGrid columns="2">
<h:outputText value="Title:"/>
<h:inputText value="#{cc.attrs.bean.title}"/>
</h:panelGrid>
</cc:implementation>
In the older Mojarra dependent approach, I instantiate the non-managed bean object, and add it directly to the composite component as an attribute and it works great (I'm using @BalusC's great but Mojarra dependent sample code from OmniFaces Component#includeCompositeComponent
):
ExtensionBeanInterface bean = Class.forName(className).newInstance();
attributes = new HashMap<String, Object>();
attributes.put("bean", bean); // Using bean object itself
[..]
UIComponent composite = application.createComponent(context, resource);
composite.getAttributes().putAll(attributes);
[..]
In JSF 2.2, I've found that I must pass a String ValueExpression
instead of my bean
object directly. I'm currently using this technique, and can't get it quite right:
FacesContext context = FacesContext.getCurrentInstance();
ELContext elContext = context.getELContext();
ValueExpression beanValExp = context.getApplication().getExpressionFactory()
.createValueExpression(elContext, "#{customBeanVE}", ExtensionBeanInterface.class);
beanValExp.setValue(elContext, bean);
String beanValExpStr = beanValExp.getExpressionString();
attributes = new HashMap<String, Object>();
attributes.put("bean", beanValExpStr); // Using VE instead of bean object
UIComponent composite = context.getApplication().getViewHandler()
.getViewDeclarationLanguage(context, context.getViewRoot().getViewId())
.createComponent(context, taglibURI, tagName, attributes);
[..]
This works great on the first "add composite", but on any following form submit, I get:
/resources/com/aadhoc/cvc/spikes/extensionsapproach/components/House.xhtml @16,49 value="#{cc.attrs.bean.title}": Target Unreachable, 'bean' returned null
I've verified that the composite's required and type attributes are working fine, and that the #{cc.attrs.bean.title}
is initially showing the bean's title. I verified with a static use of the composite component that refreshes work fine.
What's the deal, and how can I handoff the bean object so that it survives with the composite across the session?