Embedding a link (or other html) in a JSF message

前端 未结 2 1517
傲寒
傲寒 2020-12-05 19:17

I want to embed a link in a JSF message, is this possible?

When I try it, the rendered html of the h:messages tag escapes the html characters. I tried setting the e

相关标签:
2条回答
  • 2020-12-05 19:31

    Unfortunately, this is not possible in the standard JSF implementation. The component and the renderer doesn't officially support this attribute. You can however homegrow a renderer which handles this.

    Since this is a pretty common requirement/wish, I thought to take a look what's all possible.

    First some background information: JSF by default uses ResponseWriter#writeText() to write the tag body, which escapes HTML by default. We'd like to let it use ResponseWriter#write() instead like as with <h:outputText escape="false" />. We'd like to extend the MessagesRenderer of the standard JSF implementation and override the encodeEnd() method accordingly. But since the MessagesRenderer#encodeEnd() contains pretty a lot of code (~180 lines) which we prefer not to copypaste to just change one or two lines after all, I found it better to replace the ResponseWriter with a custom implementation with help of ResponseWriterWrapper wherein the writeText() is been overriden to handle the escaping.

    So, I ended up with this:

    package com.example;
    
    import java.io.IOException;
    
    import javax.faces.component.UIComponent;
    import javax.faces.context.FacesContext;
    import javax.faces.context.ResponseWriter;
    import javax.faces.context.ResponseWriterWrapper;
    import javax.faces.render.FacesRenderer;
    
    import com.sun.faces.renderkit.html_basic.MessagesRenderer;
    
    @FacesRenderer(componentFamily="javax.faces.Messages", rendererType="javax.faces.Messages")
    public class EscapableMessagesRenderer extends MessagesRenderer {
    
        @Override
        public void encodeEnd(FacesContext context, UIComponent component) throws IOException {
            final ResponseWriter originalResponseWriter = context.getResponseWriter();
    
            try {
                context.setResponseWriter(new ResponseWriterWrapper() {
    
                    @Override
                    public ResponseWriter getWrapped() {
                        return originalResponseWriter;
                    }
    
                    @Override
                    public void writeText(Object text, UIComponent component, String property) throws IOException {
                        String string = String.valueOf(text);
                        String escape = (String) component.getAttributes().get("escape");
                        if (escape != null && !Boolean.valueOf(escape)) {
                            super.write(string);
                        } else {
                            super.writeText(string, component, property);
                        }
                    }
                });
    
                super.encodeEnd(context, component); // Now, render it!
            } finally {
                context.setResponseWriter(originalResponseWriter); // Restore original writer.
            }
        }
    }
    

    In spite of the @FacesRenderer annotation, it get overriden by the default MessagesRenderer implementation. I suspect here a bug, so I reported issue 1748. To get it to work anyway, we have to fall back to the faces-config.xml:

    <render-kit>
        <renderer>
            <component-family>javax.faces.Messages</component-family>
            <renderer-type>javax.faces.Messages</renderer-type>
            <renderer-class>com.example.EscapableMessagesRenderer</renderer-class>
        </renderer>
    </render-kit>
    

    Then, to trigger it, just do:

    <h:messages escape="false" />
    

    And it works! :)


    Note: the above affects <h:messages> only. To do the same for <h:message>, just do the same, but replace anywhere "Messages" by "Message" (component family, renderer type and classnames).

    0 讨论(0)
  • 2020-12-05 19:41

    The escape="false" attributed you need is provided by the OmniFaces <o:messages> component. The OmniFaces utility library is available for JSF 2.

    I posted this solution mentioned by @BalusC's comment as an answer since this is the most straightforward solution.

    0 讨论(0)
提交回复
热议问题