Create Custom Facelets Component

posted by Jake on

Facelets is a great addition to jsf. It is very extensible, and it makes creating custom components even easier. It's fantastic. This is almost TOO easy. If you have a pile of view markup and/or client-side logic, then this is a good option. And by good, I mean awesome.

In memory of a good sandwich post, I present this to you al la phases.

1. Create stub source file

This custom component is to wrap together a many-to-many picker than I made earlier.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html
    PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
    xml:lang="en" lang="en">

<ui:component>
<!-- everything here will be in the component -->
</ui:component>

</html>

2. Create Taglib

<?xml version="1.0"?>
<!DOCTYPE facelet-taglib PUBLIC
    "-//Sun Microsystems, Inc.//DTD Facelet Taglib 1.0//EN"
    "http://java.sun.com/dtd/facelet-taglib_1_0.dtd">
<facelet-taglib>
    <namespace>http://www.lds.org/records</namespace>
    <tag>
        <tag-name>largeManyToManyPicker</tag-name>
        <source>components/large-many-to-many-picker.xhtml</source>
    </tag>
</facelet-taglib>

3. Implement the component

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html
    PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:ui="http://java.sun.com/jsf/facelets"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:r="http://www.jtsnake.com/reallycool"
    xml:lang="en" lang="en">

<ui:component>
  <style type="text/css">
        .selected-list a {
            display: block;
        }
    </style>

  <h:selectOneMenu id="list-choice" converter="#{entityConverter}">
    <f:selectItems value="#{r:convertSelectItemList(allChoices)}"/>
  </h:selectOneMenu>
  <a class="add-link-icon add-btn" href="#" style="text-decoration:none;" title="Add">&#160;</a>
  <br/>
  <h:selectManyListbox id="many-list" value="#{selectedChoices}"
             converter="#{entityConverter}" style="display:none;" required="#{requireSelection}"
             requiredMessage="#{requiredSelectionMsg}">
    <f:selectItems value="#{r:convertSelectItemList(allChoices)}"/>
  </h:selectManyListbox>

  <div class="selected-list">
    <ui:repeat var="choose" value="#{selectedChoices}">
      <a id="cit_#{r:getEntityConverterString(choose)}" class="delete-link-icon"
         href="javascript:removeSelection('#{r:getEntityConverterString(choose)}');">#{choose.label}</a>
    </ui:repeat>
  </div>

  <script src="#{request.contextPath}/scripts/jquery.selectboxes.js" type="text/javascript" charset="utf-8"></script>

  <script type="text/javascript">
    jQuery(document).ready(initLrgManyPicker);

    function initLrgManyPicker() {
      jQuery(".add-btn").click(function() {
        jQuery("[@id=#{idPrefix}:list-choice]").copyOptions("[@id=#{idPrefix}:many-list]");
        jQuery(".selected-list").append(addSelection());
      });
    }

    function addSelection() {
      var val = jQuery("[@id=#{idPrefix}:list-choice]").selectedValues();
      var label = jQuery("[@id=#{idPrefix}:list-choice]").children("[@selected]").text();
      return "&lt;a id=\"cit_" + val + "\" class=\"delete-link-icon\" href=\"javascript:removeSelection('" + val + "');\"&gt;" + label + "&lt;/a&gt;";
    }

    function removeSelection(poldiv) {
      jQuery("[@id=cit_" + poldiv + "]").remove();
      jQuery("[@id=#{idPrefix}:many-list]").deselectOptions(poldiv);
    }
  </script>
</ui:component>
</html>

4. Use the tag

<!-- ... --->
<dl>
  <dt>Recipients</dt>
  <dd>
    <r:largeManyToManyPicker allChoices="#{bean.emailBean.allRecipients}"
                 selectedChoices="#{email.sendToRecipients}"
                 requiredSelection="false"
                 idPrefix="form:emailrepeat:0" />
  </dd>
</dl>
<!-- ... --->

We're throwing in page-level scripts and styles. We're also linking to one external javascript. That external file must be available at the stated location. There's nothing special to ensure that this dependency is loaded otherwise.

Share the delectableness

About Jake Trent

Jake wrestles mean codes during the day, comes home to his beautiful wife, superstar son and delightful daughter in the evenings, and enjoys every day of it. RockyCode is one of his brain children, a prize of battle, won after a legendary struggle with his innermost thoughts and unwieldy hex digits. More by Jake

Leave a comment

blog comments powered by Disqus