[英]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
期間操作組件樹。 在此期間,所有組件都保證存在於樹中,並且通過在樹中添加/移動/移除組件來保證操作組件樹是安全的。 您可以通過UIComponent#findComponent()
在樹中找到其他組件。 您可以使用UIComponent#getParent()
和UIComponent#getChildren()
遍歷組件樹。 getChildren()
返回一個可變列表,保證在PreRenderViewEvent
期間可以安全操作。
鑒於您希望使用超鏈接包裝給定組件,並且已經存在一個JSF超鏈接組件<h:outputLink>
,它更容易擴展和重用它以保持代碼簡單和干燥。 這是一個基本的啟動示例:
@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()
調用。 它會自動照顧孩子的父母。
如果我正確理解了這個問題,你想要定義2個獨立的JSF組件來生成常見的HTML代碼。 其中一個可能的解決方案(如@BalusC所示)是組件樹操作,但這與以更自然的方式在xhtml中定義幾乎相同:
<my:banana link="http://www.banana.com">
<h:outputText value="This is an Apple" />
</my:banana>
如果出於某種需求要(重新)渲染通過引用成分for
-attribute,我建議使用JavaScript在客戶端DOM操作。 您只需要在banana
渲染器中使用適當的javascript代碼定義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>
(或任何其他)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.