2016-07-05 51 views
4

我有2個元素,香蕉和一個outputText,其中香蕉是一個自定義的JSF組件,在香蕉渲染器中,我想生成包含指定元素的HTML。自定義JSF組件渲染器:如何在另一個元素周圍渲染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」屬性對香蕉組件進行編碼,而是使用「for」屬性對其進行編碼。 我看到的最接近的例子是PrimeFaces工具提示,但我無法完全理解它如何利用「for」屬性。 http://www.primefaces.org/showcase/ui/overlay/tooltip/tooltipOptions.xhtml#

如果有人可以指向我正確的方向,這將真的很感激。謝謝。

回答

3

您可以在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.remove()通話是沒有必要的,當你已經執行children.add()。它會自動關心孩子的父母。

+0

只要蘋果出現,您的解決方案就能正常運行。儘管如此,謝謝。 – Jacob

+0

我已經修復了代碼以解決其他情況。儘管如此,謝謝。 – BalusC

0

如果我正確地理解了這個問題,您想定義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渲染定義script標籤與相應的JavaScript代碼:

@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>(或任何其他)。

+0

從問題:「你可以看到我不想在香蕉組件上進行編碼,而是在它所使用的元素上進行編碼」for「屬性」。因此,你的答案的第一部分不是'正確的'。如果您查看@BalusC答案中的代碼,您會發現它不知道outputText是如何呈現的。所以你的第二個答案的第二部分不是'增強' – Kukeltje