I´m trying to dynamically add a composite component to another UIComponent using a small adaptation of OmniFaces Faces.includeCompositeComponent(...)
. I´ve tried to recreate the behavior of this question but it´s not working.
The code for the adapted method is the following (I took it from OmniFaces v1.6)
/**
* Create and include the composite component of the given library ane
* resource name as child of the given UI component parent and return the
* created composite component. This has the same effect as using
* <code><my:resourceName></code>. The given component ID must be unique
* relative to the current naming container parent and is mandatory for
* functioning of input components inside the composite, if any.
*
* @param parent The parent component to include the composite component in.
* @param libraryName The library name of the composite component.
* @param resourceName The resource name of the composite component.
* @param id The component ID of the composite component.
* @return The created composite component, which can if necessary be further
* used to set custom attributes or value expressions on it.
* @since 1.5
*/
public static UIComponent includeCompositeComponent(UIComponent parent, String libraryName, String resourceName, String id)
{
// Prepare.
FacesContext context = FacesContext.getCurrentInstance();
Application application = context.getApplication();
FaceletContext faceletContext = (FaceletContext) context.getAttributes().get(FaceletContext.FACELET_CONTEXT_KEY);
// 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.
// 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);
}
finally
{
parent.popComponentFromEL(context);
}
return composite;
}
And this is the way I´m trying to use it
JSFUtils.includeCompositeComponent(panelGroup, "comp",
"../inc-templates/test.xhtml", JSFUtils.createUniqueId());
Also, I created the "test.xhtml" file and placed it under WEB-INF/inc-templates/test.xhtml . Here is its content:
test.xhtml
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:cc="http://java.sun.com/jsf/composite"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:p="http://primefaces.org/ui"
xmlns:composite="http://java.sun.com/jsf/composite">
<!-- INTERFACE -->
<cc:interface>
</cc:interface>
<!-- IMPLEMENTATION -->
<cc:implementation>
<h:outputText value="TEST"/>
</cc:implementation>
</html>
Last but not least, I´ve added the test.xhtml tag to the taglib.xml, like this:
<tag>
<tag-name>test</tag-name>
<source>../inc-templates/test.xhtml</source>
</tag>
The thing is, when I run the code and try to insert the component I get the following exception
Caused by: java.lang.NullPointerException: Argumentfehler: Parameter componentResource ist null
at com.sun.faces.util.Util.notNull(Util.java:314)
at com.sun.faces.application.ApplicationImpl.createComponent(ApplicationImpl.java:928)
at org.my.JSFUtils.includeCompositeComponent(JSFUtils.java:496)
and the line at org.my.JSFUtils.includeCompositeComponent(JSFUtils.java:496) refers exactly to the line of code UIComponent composite = application.createComponent(context, resource);
, which in fact reveals that the resource is not being created in the previous line: Resource resource = application.getResourceHandler().createResource(resourceName, libraryName);
Does anyone have any idea why could this be happening?. I´ve even tried changing the location of the resource to inc-templates/test.xhtml and even just test.xhtml when invoking the method, but nothing works... I always get the same error.
By the way, I´m trying to deploy in Websphere 8 and I´m using ICEFaces 3.
Thanks in advance!
It seems that you're mixing composite components with tag files. To see and understand the difference between both head to this answer and the links inside it: When to use <ui:include>, tag files, composite components and/or custom components?
Your XHTML file clearly represents a composite component. However, you're treating it like a tag file. It's been placed in /WEB-INF
folder like a tag file instead of /resources
folder like a true composite. It's been registered in a .taglib.xml
file like a tag file instead of configurationless like a true composite.
It's not entirely clear what exactly you want. Do you want to programmatically include an include file, or a tag file, or a composite component? Let's assume that you actually want to programmatically include a composite component (otherwise the whole usage of includeCompositeComponent()
is completely pointless). In that case, move the XHTML file to /resources
folder like as shown in all composite component tutorials (in such way that it also really works when you use <my:test>
in any arbitrary XHTML template client):
WebContent
|-- WEB-INF
| `-- lib
|-- resources
| `-- mycomponents
| `-- test.xhtml <---
`-- some.xhtml
Get rid of the unnecessary entry in .taglib.xml
and finally include it using library name of null
and resource name of /mycomponents/test.xhtml
.
includeCompositeComponent(parent, null, "/mycomponents/test.xhtml", uniqueId);