如何以编程方式访问使用 <ui:define> 创建的内容?
How can I access the content of something created with <ui:define> programmatically?
在上下文中的什么位置可以找到有关使用 <ui:define>
构建的内容的信息?我想访问在我的 bean 中用 <ui:define name="title">Some title</ui:define>
定义的页面标题。
为了说明我的问题,我可以访问用
定义的变量
<ui:param name="myVariable" value="This is my variable!"/>
通过查看 EL 上下文中的变量映射器,像这样
VariableMapper variableMapper = elContext.getVariableMapper();
String myVariable = variableMapper.resolveVariable("myVariable").getValue(elContext).toString();
这适用于 <ui:param>
,但它如何适用于 <ui:define>
?
通过标准 API 这是不可能的。 Xtreme Biker 发布了一个绝妙的技巧,即在 <ui:insert>
中指定了一个“默认”<ui:param>
值,当 <ui:define>
实际上被指定为答案时,该值将被覆盖(因此不存在)Test if ui:insert has been defined in the template client
一个(hacky)替代方案是为该作业创建一个自定义标签处理程序。 <ui:define>
的名称收集在 <ui:composition>
后面的 CompositionHandler
标记处理程序 class 的 Map handlers
字段中。这是(不幸的)具体实现,Mojarra and MyFaces 有自己的实现,Mojarra 将字段命名为 handlers
和 MyFaces _handlers
.
由于该字段只是 protected
,最干净的方法是扩展 CompositionHandler
taghandler class 并至少公开 apply()
方法中的键集作为属性FaceletContext
。但是,由于 CompositionHandler
class 本身被声明为 final
,我们不能对其进行子class。因此,我们不能绕过将其包装为委托并使用一些反射 hackery 来获取该字段。
这是一个基于 Mojarra 的启动示例,它在 Map<String, Boolean>
中收集所有声明的 <ui:define>
处理程序名称,以便您可以在 EL 中很好地使用它们 #{defined.foo ? '...' : '...'}
分别 #{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);
}
}
在自定义中按如下方式注册my.taglib.xml
:
<tag>
<tag-name>composition</tag-name>
<handler-class>com.example.DefineAwareCompositionHandler</handler-class>
</tag>
您可以如下使用它:
<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>
同样,这很老套(因为它是特定于实现的),我不建议使用它。
另请参阅:
- Custom Facelet component in JSF
在上下文中的什么位置可以找到有关使用 <ui:define>
构建的内容的信息?我想访问在我的 bean 中用 <ui:define name="title">Some title</ui:define>
定义的页面标题。
为了说明我的问题,我可以访问用
定义的变量<ui:param name="myVariable" value="This is my variable!"/>
通过查看 EL 上下文中的变量映射器,像这样
VariableMapper variableMapper = elContext.getVariableMapper();
String myVariable = variableMapper.resolveVariable("myVariable").getValue(elContext).toString();
这适用于 <ui:param>
,但它如何适用于 <ui:define>
?
通过标准 API 这是不可能的。 Xtreme Biker 发布了一个绝妙的技巧,即在 <ui:insert>
中指定了一个“默认”<ui:param>
值,当 <ui:define>
实际上被指定为答案时,该值将被覆盖(因此不存在)Test if ui:insert has been defined in the template client
一个(hacky)替代方案是为该作业创建一个自定义标签处理程序。 <ui:define>
的名称收集在 <ui:composition>
后面的 CompositionHandler
标记处理程序 class 的 Map handlers
字段中。这是(不幸的)具体实现,Mojarra and MyFaces 有自己的实现,Mojarra 将字段命名为 handlers
和 MyFaces _handlers
.
由于该字段只是 protected
,最干净的方法是扩展 CompositionHandler
taghandler class 并至少公开 apply()
方法中的键集作为属性FaceletContext
。但是,由于 CompositionHandler
class 本身被声明为 final
,我们不能对其进行子class。因此,我们不能绕过将其包装为委托并使用一些反射 hackery 来获取该字段。
这是一个基于 Mojarra 的启动示例,它在 Map<String, Boolean>
中收集所有声明的 <ui:define>
处理程序名称,以便您可以在 EL 中很好地使用它们 #{defined.foo ? '...' : '...'}
分别 #{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);
}
}
在自定义中按如下方式注册my.taglib.xml
:
<tag>
<tag-name>composition</tag-name>
<handler-class>com.example.DefineAwareCompositionHandler</handler-class>
</tag>
您可以如下使用它:
<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>
同样,这很老套(因为它是特定于实现的),我不建议使用它。
另请参阅:
- Custom Facelet component in JSF