Reputation: 868
Where in the contexts can I find the information for something built with a <ui:define>
? I want to access a page title that has been defined with <ui:define name="title">Some title</ui:define>
in my bean.
To illustrate my question, I can access a variable defined with
<ui:param name="myVariable" value="This is my variable!"/>
by looking at the variable mapper in the EL context, like this
VariableMapper variableMapper = elContext.getVariableMapper();
String myVariable = variableMapper.resolveVariable("myVariable").getValue(elContext).toString();
This works for <ui:param>
, but how is it done for <ui:define>
?
Upvotes: 2
Views: 1271
Reputation: 1109625
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.
Upvotes: 2