自定义 JSF 组件渲染器:如何围绕另一个元素渲染 HTML
Custom JSF component renderer: how to render HTML around another element
我有 2 个元素,banana 和一个 outputText,其中 banana 是一个自定义的 JSF 组件,在 banana 渲染器中,我想生成 HTML 包含指定的元素。
xhtml:
<h:outputText id="theApple" value="This is an Apple" />
.
.
.
<my:banana for="theApple" link="http://www.banana.com" />
bananaRenderer(将目标元素包含在锚链接中):
@Override
public void encodeBegin(FacesContext context, UIComponent component) throws IOException {
ResponseWriter responseWriter = context.getResponseWriter();
Banana banana = Banana.class.cast(component);
responseWriter.startElement("a", null);
responseWriter.writeAttribute("id", banana.getClientId(context), null);
responseWriter.writeAttribute("href", banana.getLink(), null);
}
@Override
public void encodeEnd(FacesContext context, UIComponent component) throws IOException {
ResponseWriter responseWriter = context.getResponseWriter();
Banana banana = Banana.class.cast(component);
responseWriter.endElement("a");
}
我想达到的目标:
<a href="http://www.banana.com">This is an Apple</a>
.
.
.
如您所见,我不想在香蕉组件上编码,而是在它使用 "for" 属性定位的元素上编码。
我看到的最接近的例子是 PrimeFaces 工具提示,但我不太明白它是如何利用 "for" 属性的。
http://www.primefaces.org/showcase/ui/overlay/tooltip/tooltipOptions.xhtml#
如果有人能指出正确的方向,我将不胜感激。谢谢。
您可以在 PreRenderViewEvent
. During that moment, all components are guaranteed to be present in the tree, and it's guaranteed safe to manipulate the component tree by adding/moving/removing components in the tree. You can find other components in the tree by UIComponent#findComponent()
. You can traverse the component tree using UIComponent#getParent()
and UIComponent#getChildren()
期间操作组件树。 getChildren()
returns 一个可变列表,保证在 PreRenderViewEvent
期间可以安全操作。
鉴于您希望用超链接包装给定组件,并且已经存在一个 JSF 超链接组件 <h:outputLink>
,扩展和重用它更容易,以保持代码简单和 DRY .这是一个基本的启动示例:
@FacesComponent(createTag=true)
public class Banana extends HtmlOutputLink implements SystemEventListener {
public Banana() {
getFacesContext().getViewRoot().subscribeToViewEvent(PreRenderViewEvent.class, this);
}
@Override
public boolean isListenerForSource(Object source) {
return source instanceof UIViewRoot;
}
@Override
public void processEvent(SystemEvent event) throws AbortProcessingException {
String forClientId = (String) getAttributes().get("for");
if (forClientId != null) {
UIComponent forComponent = findComponent(forClientId);
List<UIComponent> forComponentSiblings = forComponent.getParent().getChildren();
int originalPosition = forComponentSiblings.indexOf(forComponent);
forComponentSiblings.add(originalPosition, this);
getChildren().add(forComponent);
}
}
}
<!DOCTYPE html>
<html lang="en"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:my="http://xmlns.jcp.org/jsf/component"
>
<h:head>
<title>SO question 38197494</title>
</h:head>
<h:body>
<h:outputText id="theApple" value="This is an Apple" />
<my:banana for="theApple" value="http://www.banana.com" />
</h:body>
</html>
请注意,如果您已经执行了 children.add()
,则无需显式 children.remove()
调用。它会自动处理 child 的 parent。
如果我正确理解了这个问题,您想要定义 2 个单独的 JSF 组件来生成通用的 HTML 代码。一种可能的解决方案(如@BalusC 所示)是组件树操作,但这与以更自然的方式在 xhtml 中定义几乎相同:
<my:banana link="http://www.banana.com">
<h:outputText value="This is an Apple" />
</my:banana>
如果出于某些需要您想要(重新)呈现通过 for
属性引用的组件,我建议在客户端使用 javascript 进行 DOM 操作。您只需要使用适当的 javascript 代码在 banana
渲染器中定义 script
标签:
@Override
public void encodeEnd(FacesContext context, UIComponent component) throws IOException {
ResponseWriter writer = context.getResponseWriter();
writer.startElement("script", null);
writer.writeAttribute("type", "text/javascript", null);
// find DOM-element by 'for'-attribute, and wrap it with a hyperlink
writer.endElement("script");
}
这几乎就是 Primefaces 工具提示的构建方式。
好处是 banana
渲染器不知道 <h:outputText>
(或任何其他)将如何渲染。
我有 2 个元素,banana 和一个 outputText,其中 banana 是一个自定义的 JSF 组件,在 banana 渲染器中,我想生成 HTML 包含指定的元素。
xhtml:
<h:outputText id="theApple" value="This is an Apple" />
.
.
.
<my:banana for="theApple" link="http://www.banana.com" />
bananaRenderer(将目标元素包含在锚链接中):
@Override
public void encodeBegin(FacesContext context, UIComponent component) throws IOException {
ResponseWriter responseWriter = context.getResponseWriter();
Banana banana = Banana.class.cast(component);
responseWriter.startElement("a", null);
responseWriter.writeAttribute("id", banana.getClientId(context), null);
responseWriter.writeAttribute("href", banana.getLink(), null);
}
@Override
public void encodeEnd(FacesContext context, UIComponent component) throws IOException {
ResponseWriter responseWriter = context.getResponseWriter();
Banana banana = Banana.class.cast(component);
responseWriter.endElement("a");
}
我想达到的目标:
<a href="http://www.banana.com">This is an Apple</a>
.
.
.
如您所见,我不想在香蕉组件上编码,而是在它使用 "for" 属性定位的元素上编码。 我看到的最接近的例子是 PrimeFaces 工具提示,但我不太明白它是如何利用 "for" 属性的。 http://www.primefaces.org/showcase/ui/overlay/tooltip/tooltipOptions.xhtml#
如果有人能指出正确的方向,我将不胜感激。谢谢。
您可以在 PreRenderViewEvent
. During that moment, all components are guaranteed to be present in the tree, and it's guaranteed safe to manipulate the component tree by adding/moving/removing components in the tree. You can find other components in the tree by UIComponent#findComponent()
. You can traverse the component tree using UIComponent#getParent()
and UIComponent#getChildren()
期间操作组件树。 getChildren()
returns 一个可变列表,保证在 PreRenderViewEvent
期间可以安全操作。
鉴于您希望用超链接包装给定组件,并且已经存在一个 JSF 超链接组件 <h:outputLink>
,扩展和重用它更容易,以保持代码简单和 DRY .这是一个基本的启动示例:
@FacesComponent(createTag=true)
public class Banana extends HtmlOutputLink implements SystemEventListener {
public Banana() {
getFacesContext().getViewRoot().subscribeToViewEvent(PreRenderViewEvent.class, this);
}
@Override
public boolean isListenerForSource(Object source) {
return source instanceof UIViewRoot;
}
@Override
public void processEvent(SystemEvent event) throws AbortProcessingException {
String forClientId = (String) getAttributes().get("for");
if (forClientId != null) {
UIComponent forComponent = findComponent(forClientId);
List<UIComponent> forComponentSiblings = forComponent.getParent().getChildren();
int originalPosition = forComponentSiblings.indexOf(forComponent);
forComponentSiblings.add(originalPosition, this);
getChildren().add(forComponent);
}
}
}
<!DOCTYPE html>
<html lang="en"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:my="http://xmlns.jcp.org/jsf/component"
>
<h:head>
<title>SO question 38197494</title>
</h:head>
<h:body>
<h:outputText id="theApple" value="This is an Apple" />
<my:banana for="theApple" value="http://www.banana.com" />
</h:body>
</html>
请注意,如果您已经执行了 children.add()
,则无需显式 children.remove()
调用。它会自动处理 child 的 parent。
如果我正确理解了这个问题,您想要定义 2 个单独的 JSF 组件来生成通用的 HTML 代码。一种可能的解决方案(如@BalusC 所示)是组件树操作,但这与以更自然的方式在 xhtml 中定义几乎相同:
<my:banana link="http://www.banana.com">
<h:outputText value="This is an Apple" />
</my:banana>
如果出于某些需要您想要(重新)呈现通过 for
属性引用的组件,我建议在客户端使用 javascript 进行 DOM 操作。您只需要使用适当的 javascript 代码在 banana
渲染器中定义 script
标签:
@Override
public void encodeEnd(FacesContext context, UIComponent component) throws IOException {
ResponseWriter writer = context.getResponseWriter();
writer.startElement("script", null);
writer.writeAttribute("type", "text/javascript", null);
// find DOM-element by 'for'-attribute, and wrap it with a hyperlink
writer.endElement("script");
}
这几乎就是 Primefaces 工具提示的构建方式。
好处是 banana
渲染器不知道 <h:outputText>
(或任何其他)将如何渲染。