This is not possible via standard API. Xtreme Biker has posted a brilliant trick whereby a "default" <ui:param>
value is specified inside the <ui:insert>
which would be overriden (and thus absent) when a <ui:define>
is actually specified as answer on Test if ui:insert has been defined in the template client
A (hacky) alternative would be to create a custom taghandler for the job. The <ui:define>
s are by their name collected in Map handlers
field of the CompositionHandler
taghandler class behind <ui:composition>
. This is (unfortunately) implementation specific, Mojarra and MyFaces have their own implementations whereby Mojarra has named the field handlers
and MyFaces _handlers
.
As the field is just protected
, cleanest would be to just extend the CompositionHandler
taghandler class and expose at least the keyset in the apply()
method as attribute of FaceletContext
. However, as the CompositionHandler
class itself is declared final
, we can't subclass it. Therefore, we can't go around wrapping it as a delegate and use some reflection hackery to grab the field anyway.
Here's a kickoff example based on Mojarra which collects all declared <ui:define>
handler names in a Map<String, Boolean>
so that you can nicely use them in EL like so #{defined.foo ? '...' : '...'}
respectively #{not defined.foo ? '...' : '...'}
.
public class DefineAwareCompositionHandler extends TagHandlerImpl implements TemplateClient {
private CompositionHandler delegate;
private Map<String, Boolean> defined;
@SuppressWarnings("unchecked")
public DefineAwareCompositionHandler(TagConfig config) {
super(config);
delegate = new CompositionHandler(config);
try {
Field field = delegate.getClass().getDeclaredField("handlers");
field.setAccessible(true);
Map<String, DefineHandler> handlers = (Map<String, DefineHandler>) field.get(delegate);
if (handlers != null) {
defined = new HashMap<>();
for (String name : handlers.keySet()) {
defined.put(name, true);
}
}
}
catch (Exception e) {
throw new FaceletException(e);
}
}
@Override
public void apply(FaceletContext ctx, UIComponent parent) throws IOException {
ctx.setAttribute("defined", defined);
delegate.apply(ctx, parent);
}
@Override
public boolean apply(FaceletContext ctx, UIComponent parent, String name) throws IOException {
return delegate.apply(ctx, parent, name);
}
}
Register it as follows in your custom my.taglib.xml
:
<tag>
<tag-name>composition</tag-name>
<handler-class>com.example.DefineAwareCompositionHandler</handler-class>
</tag>
You could make use of it as below:
<my:composition
xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:my="http://example.com/ui"
>
<ui:insert name="foo">
...
</ui:insert>
<div class="#{defined.foo ? 'style1' : 'style2'}">
...
</div>
</my:composition>
Again, this is hacky (as it's implementation specific), I'd not recommend using it.
See also:
title
had been defined and if not, set a generic page title.. and I am curious, how this can be done programmatically. I was searching for the component tree with the keytitle
in the contexts, but could not find it. – Baffle<ui:define name="title">
for an<ui:insert name="title">
has been defined in the template client, regardless of the content? I.e. you're only interested in having a booleantrue
orfalse
based on that which you can then use elsewhere in the template? If so, is this then acceptable as dupe? stackoverflow.com/questions/26070704/… – TheomaniaAppName - PageTitle
. Some pages do not define aPageTitle
, they getAppName
as a generic title. Some do and for these pages the definedPageTitle
shall be concatenated with theAppName
anddash
prefix. As I was working on this, I tried to retrieve the content of theui:define
in a bean, to see how and where this information is stored in a context and could not find it. I spent hours trying to solve this and I could not find any relevant information about this on the net. Any hints in the right direction are also very welcome. – Baffle