包含动态生成的facelet的方法

时间:2010-09-10 07:04:54

标签: java jsf dynamic facelets el

在当前项目中,我需要创建一个面板,其中包含用户在应用程序中其他位置创建的HTML内容。这个内容可以像这样轻松插入:

<h:outputText value="#{myBean.dynamicHTMLContent}" escape="false"/>

示例内容:

<p>User text</p>

现在我们需要为用户提供更多自由,并允许他在HTML代码中使用令牌,稍后将由应用程序解决:

<p>User text</p><p>User image: {niceImage}</p>

应用程序解析 myBean.dynamicHTMLContent 中的用户内容,并用

替换 {niceImage(param)}
<a4j:mediaOutput element="img" createContent="{myBean.generateNiceImage}"/>

这已经是一个facelet代码段,无法在 h:outputText 中进行评估和呈现。

我正在寻找一种在EL表达式尚未评估的阶段在facelet中包含此类动态内容的好方法。像

这样的东西
<ui:include src="src"/>

但对于动态组件来说,这将是最好的解决方案。

有什么想法吗?

4 个答案:

答案 0 :(得分:3)

我同意user423943为此创建组件的想法。但是,我会延长<h:outputText>。在您的情况下,您将无需做很多工作。首先,创建一个my.taglib.xml文件:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE facelet-taglib PUBLIC "-//Sun Microsystems, Inc.//DTD Facelet Taglib 1.0//EN" "facelet-taglib_1_0.dtd">
<facelet-taglib>
    <namespace>http://my.components/jsf</namespace>
    <tag>
        <tag-name>myComponent</tag-name>
        <component>
            <component-type>my.component.myComponent</component-type>
            <renderer-type>my.renderkit.myComponent</renderer-type>
        </component>
    </tag>
</facelet-taglib>

此文件只需要存在于应用程序的类路径中,它将由Facelets自动加载(因为它以.taglib.xml结尾)。

然后,在faces-config.xml中定义此组件的Java类:

<component>
    <component-type>my.component.myComponent</component-type>
    <component-class>my.package.component.MyHtmlComponent</component-class>
</component>
<render-kit>
    <render-kit-id>HTML_BASIC</render-kit-id>
    <renderer>
        <component-family>javax.faces.Output</component-family>
        <renderer-type>my.renderkit.myComponent</renderer-type>
        <renderer-class>my.package.component.MyHtmlComponentRenderer</renderer-class>
    </renderer>

然后,您将必须创建两个类:

  • my.package.component.MyHtmlComponent将延长javax.faces.component.html.HtmlInputText并且不做任何其他事情。
  • my.package.component.MyHtmlComponentRenderer将扩展com.sun.faces.renderkit.html_basic.TextRenderer类。

您的渲染器类将完成所有工作,方法是生成组件值的HTML代码,与<h:outputText>完全相同。您可以查看本部分中涉及的HtmlBasicRenderer.encodeEnd(FacesContext, UIComponent)TextRenderer.getEndTextToRender(FacesContext, UIComponent, String)方法。 当然,当您在文本中遇到{niceImage}代码时,只需生成HTML img代码即可。为此,您可以使用ResponseWriter的适当方法来构建HTML标记和属性:

writer.startElement("img", component);
writer.writeAttribute("src", urlToImage);
writer.endElement("img");

创建完所有内容后,您必须在JSF页面中使用新组件:

<html xmlns:my="http://my.components/jsf">
    ...
    <my:myComponent value="#{myBean.dynamicHTMLContent}" escape="false"/>
    ...

除了user423943提供的链接之外,还有两个可以帮助您的链接:

http://www.jsftutorials.net/helpDesk/standardRenderKit_component-class_renderer-slass.html

http://www.jsftutorials.net/helpDesk/standardRenderKit_component-type_renderer-type.html

对于所有HTML JSF组件,您会发现它们的类型和类。

答案 1 :(得分:1)

我认为,这种复杂的原因是#{myBean.dynamicHTMLContent}不是HTML内容,而是JSF内容。我认为最灵活的解决方案是编写自己的JSF组件。也许有人会纠正我,但我认为没有办法替换像{niceImage} JSF代码这样的文字。

有一些关于此的文章:

我不是JSF专家,但你可能会:

  • 延长org.ajax4jsf.MediaOutput
  • parse out all the text in curly braces
  • 使用niceImage或其他
  • 的引用替换#{myBean.generateNiceImage}之类的内容
  • 将实际工作转发给超类org.ajax4jsf.MediaOutput

希望有所帮助!

答案 2 :(得分:1)

您也可以使用includeFacelet(UIComponent, URL)来包含动态生成的facelets。诀窍是使用data网址方案和自定义URLStreamHandler

String encoded = Base64.encodeBase64String(myDynamicFacelet.getBytes());
context.includeFacelet(uiComponent, new URL(null, "data://text/plain;base64," + encoded, new DataStreamHandler()));

如果您有data://个网址的通用处理程序,那么它是最佳选择。我只需要这个特定用例的处理程序,所以它非常有限:

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLStreamHandler;

import org.apache.commons.codec.binary.Base64;

public class DataStreamHandler extends URLStreamHandler {

  private static class DataURLConnection extends URLConnection {

    @Override
    public InputStream getInputStream() throws IOException {
      return new ByteArrayInputStream(this.content.getBytes());
    }

    private static String PREFIX = "data://text/plain;base64,";
    private static int PREFIX_LEN = PREFIX.length();

    protected DataURLConnection(URL url) {
      super(url);
      this.url = url;

      String encoded = this.url.toString().substring(PREFIX_LEN);
      this.content = new String(Base64.decodeBase64(encoded));
    }

    @Override
    public void connect() throws IOException {
      // Do nothing
    }

    private URL url;
    private String content;
  }

  @Override
  protected URLConnection openConnection(URL url) throws IOException {
    return new DataURLConnection(url);
  }
}

答案 3 :(得分:0)

最后,我采用简单的方法,用相应的JSF元素替换用户HTML中的所有自定义(花括号)标记,并生成临时的ui:composition facelet文件:

public String getUserHtmlContentPath() {

   File temp = File.createTempFile("userContent", ".tmp");
   temp.deleteOnExit();

   FileWriter fw = new FileWriter(temp);
   fw.write(getUserHtmlContentComposition());
   fw.close();

   return "file://" + temp.getAbsolutePath(); 
}

并在父面孔中:

<ui:include src="#{myBean.userHtmlContentPath}"/>