Search code examples
jsfcomponentsfacelets

Quick way to create JSF custom component


I know of two ways of creating custom JSF components: 1. Native JSF way: creating JSF component class, tag, etc. 2. Facelets way: defining component in a xhtml file and then creating appropriate decrption in facelets taglib.

Currently I work on a project in which introducing facelets is unfortunately out of the question. On the other hand, creating custom components the standard JSF way seems like a pain in the ass.

Is there maybe a third party library that allows creating custom components in the way similar to facelets but doesn't entail the need of using non-standard renderer?


Solution

  • You can do a limited amount of templating using (for example) jsp:include and f:subview.

    Alternatively, you can extend a UIComponent overriding selected methods and then provide it via an existing tag and a managed bean using the binding attribute. This still requires a reasonably detailed understanding of component development (and the consequences of this choice), but could cut down the number of files/volume of code significantly.

    This approach is a bit of a hack, but might be OK for short-term stuff. You wouldn't do it for component libraries you want to distribute or components requiring long term maintenance.

    The new component:

    public class QuickComponent extends HtmlOutputText {
      @Override public void encodeAll(FacesContext context) throws IOException {
        ResponseWriter writer = context.getResponseWriter();
        writer.writeText("I'm not really a HtmlOutputText", null);
        for (UIComponent kid : getChildren()) {
          if (kid instanceof UIParameter) {
            UIParameter param = (UIParameter) kid;
            writer.startElement("br", this);
            writer.endElement("br");
            writer.writeText(param.getName() + "=" + param.getValue(), null);
          }
        }
      }
    }
    

    The bean providing an instance:

    /**Request-scope managed bean defined in faces-config.xml*/
    public class QuickComponentProviderBean {
      private QuickComponent quick;
    
      public void setQuick(QuickComponent quick) {
        this.quick = quick;
      }
    
      public QuickComponent getQuick() {
        if (quick == null) {
          quick = new QuickComponent();
        }
        return quick;
      }
    }
    

    Note: don't reuse a single bean property for multiple tags in your views, or they'll reference the same object instance.

    Adding the new component to the view:

    <h:outputText binding="#{quickComponentProviderBean.quick}">
      <f:param name="Hello" value="World" />
    </h:outputText>
    

    Note: the attributes that can be defined have not changed. They're fixed by the TLD.