« Previous day (Jun 18, 2005) | Main | Next day (Jun 20, 2005) »

20050619 Sunday June 19, 2005

How to Write a JSF Component

As JSF is gaining popularity, more JSF components are being written. In fact TopCoder is running a competition on writing components. (Sun is a sponsor.)

However, some of the components written work, but do not work well in tools like Creator, or do not work well when used in conjunction with CSS. In this blog entry I will provide some hints that will make sure your components do not fall in that category.

How to actually write a JSF component is covered in many places; it's not hard. You need to write 3-4 classes, plus write a .tld file and a faces-config file with some information about your component.

Here's a document which takes you through this process.

However, there are some additional Rules You Should Follow when you write your component.

These rules serve two purposes:

Here they are:

  1. First and foremost, you MUST use the ResponseWriter's startElement, endElement and writeAttribute methods. Do NOT write out markup by simply streaming out HTML bytes linearly using the write method. There is one minor exception to this rule: some components need to emit HTML provided by the user, such as the HtmlOutputText component when its Escape property is set to false. In this limited scenario you should use write.
  2. Always pass in a component reference to the startElement method on the ResponseWriter. E.g. if your UIComponent is a PanelGrid, and you want to emit <table><tr><td>... you would use
       writer.startElement("table", gridpanel);
       writer.startElement("tr", gridpanel);
       writer.startElement("td", gridpanel);
    
  3. Every component should have a style property, which will render to a style attribute on the rendered HTML markup. This will allow tools like Creator to position and size the component (using CSS). It is vital that you remember to replicate this style attribute on your top level rendered HTML tag. However, there are some complications...
    1. In the normal case, you render a single top level HTML element (possibly with children) from the JSP tag. In this case, simply duplicate the value of the JSP tag's style attribute to the top level HTML tag attribute. For example, if you have <h:commandButton style="width: 100px"/> in the JSP, you would render <input type="submit" style="width: 100px"/>
    2. If you need to render multiple top level HTML tags, but only one of them is visual, duplicate the style attribute on the one visual tag. For example, your component may want to render a <script> tag before it renders its one visual element, <table>, and then perhaps another non visual element, an <input> of type hidden. In this case, simply emit the <script>, then emit the <table> with the style property value from the JSF component, and finally emit the hidden <input>.
    3. If you need to render more than one visual element as siblings at the top level, you need to wrap these in a single container component, such as a <span> or a <div>. (Use span only if all the children being rendered are inline elements.) The style property should be placed only on this outer, wrapping <span> or <div>.
  4. Don't throw exceptions from your renderer! Even if for example value is a required property, don't throw a configuration exception just because it's null when you're rendering! If you really want your component to alert the user to the problem, I suppose it's okay to throw an exception when the component is used at runtime, but at designtime, it's quite normal for a component not to be correctly configured yet - after all, the user may just have dropped the component and is about to configure it. If necessary, you can have code in your renderer which checks if the component renderer is being used at designtime:
      if (java.beans.Beans.isDesignTime()) {
          // Designtime - better render error handling here
          ...
      } else {
          // Runtime - throw your exceptions if you must
          ...
      }
    
    One thing you might consider doing is having the component render exactly the problem message to the user. As an example, in Creator, if you drop the Message component, it will tell you if it is not bound to anything (since the for property is null, so the component is not yet configured.)

Once you have written your component you should package it up such that it can easily be imported into Creator and used out of the box by your users. That is also covered in the component library article I linked to earlier.

Please let me know if you develop components that work well with Creator!

(2005-06-19 12:07:31.0) Permalink Comments [1]