簡體   English   中英

自定義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期間操作組件樹。 在此期間,所有組件都保證存在於樹中,並且通過在樹中添加/移動/移除組件來保證操作組件樹是安全的。 您可以通過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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM