Sunday, June 19, 2005

How to Write a JSF Component

WARNING: This blog entry was imported from my old blog on blogs.sun.com (which used different blogging software), so formatting and links may not be correct.


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:


  • They ensure that your component will work right when it is
    sized and positioned using CSS properties.

  • They ensure that your component will work well at designtime
    in a tool like Creator.




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!


1 comment:

  1. hi;
    i'm currently using jsf and develop some apps, my personal thought about JSF is its not easy as much as asp.net is. it needs some serious revisiotions, especially in backing bean development and design-time component behavior areas.
    Actually my personal idea is JSF will never get such a popularity that asp.net had already gained.

    ReplyDelete