piątek, 3 maja 2013

JSF2 - własne komponenty

JSF2 pozwalaja w bardzo szybki i prosty sposób tworzyć własne komponenty, dzięki czemu nie musimy powielać kodu w kilku miejscach na stronach xhtml'owych. Pytanie tylko kiedy warto poświęcić trochę czasu i stworzyć własny komponent? Oczywiście nie ma dobrej odpowiedzi na takie pytanie, wszystko zależy od tego jak dużo i jak długo będziemy dany system/aplikację utrzymywać. Pewnie znajdą się systemy w których stosowanie własnych komponentów jest niepotrzebne, jak i takie, gdzie przykrywa się standardowe jsf'owe kontrolki, własnymi, aby mieć większą kontrolę nad ich wyglądem/zachowaniem. Warto zwrócić również uwagę, że dobrze ułożone komponenty, nie tylko ułatwiają pisanie kodu, ale również jego czytanie.

Oto kilka przykładów, gdzie moim zdaniem warto się zastanowić nad stworzeniem własnego taga:
  • komponent pojawia się w wielu miejscach w systemie (np. inputy formularza, panele ze zdefiniowanym nagłówkiem, popup'y, itp.)
  • zestaw kontrolek do obsługi ValueObject tj. MoneyVO, PeselVO, PostCodeVO, itd.
  • bardzo złożony fragment kodu, który może i teoretycznie nie wymaga stworzenia własnego tag'a, ale dzięki czemu nasz kod stanie się o wiele bardziej czytelny

Jak stworzyć własny tag
Zaczynamy od stworzenia pliku, który będzie naszym szablonem. Definiujemy w nim wszystkie komponenty, które będą tworzyły nasz komponent.
Ważne fragmenty:
  • <ui:param> czyli możliwość definiowania własnych parametrów, dzięki którym nasz kod jest bardziej czytelny
  • <ui:insert> tag, który definiuje nam pewną sekcję, którą możemy wykorzystać/zastąpić w momencie wykorzystania naszego taga. Atrybut name defiuje nazwę sekcji.
  • #{name} zmienna name, która może zostać ustawiona w momencie wykorzystania taga
Mój plik znajduje się w \WEB-INF\tags\com\blogspot\mkorwel\formInputText.xhtml.
<?xml version="1.0" encoding="UTF-8"?>
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
 xmlns:h="http://java.sun.com/jsf/html"
 xmlns:ui="http://java.sun.com/jsf/facelets">

 <ui:param name="labelMsg" value="#{not empty labelMsg ? labelMsg : 'defaultLabel'}" />

 <div>
  <ui:insert name="label">
   <div style="width: 150px; float:left;">
    <h:outputText id="#{name}_label" value="#{labelMsg}" />
   </div>
  </ui:insert>
 
  <h:inputText id="#{name}_inputText" value="#{value}" />
 </div>
</ui:composition>
Następnym krokiem jest stworzenie facletowego tagliba, w którym definiujemy:
  • namespace,
  • miejsce położenia naszych tagów.
U mnie, plik znajduje się w: WEB-INF\my.taglib.xml.
<?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://mkorwel.blogspot.com/components</namespace>
    <tag>
  <tag-name>formInputText</tag-name>
  <source>tags/com/blogspot/mkorwel/formInputText.xhtml</source>
    </tag>
</facelet-taglib>
Ostatnim krokiem jest zarejestrowanie naszego tagliba w web.xml.
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
      version="3.0"> 
       
 <context-param>
     <param-name>facelets.LIBRARIES</param-name>
     <param-value>/WEB-INF/my.taglib.xml</param-value>
  </context-param>

</web-app>
U mnie struktura plików wygląda następująco:


Wykorzytanie
Poniżej krótki fragment kodu, który przedstawia przykładowe wykorzystanie tagu formInputText.
Ważne fragmenty:
  • <ui:define> możliwość nadpisania sekcji <ui:insert> zdefiniowanej wewnątrz tagu. Atrybut name wskazuje którą sekcję chcemy nadpisać.
<?xml version="1.0" encoding="UTF-8"?>
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
 xmlns:ui="http://java.sun.com/jsf/facelets"
 xmlns:f="http://java.sun.com/jsf/core"
 xmlns:h="http://java.sun.com/jsf/html"
 xmlns:my="http://mkorwel.blogspot.com/components"
 template="/WEB-INF/templates/default.xhtml">
 <ui:define name="content">

  <h:form>

   <my:formInputText name="example1" labelMsg="Example1: "
    value="#{myFormController.example1}" />
   
   <my:formInputText name="example2" labelMsg="Example2: "
    value="#{myFormController.example2}" />
  
   <my:formInputText name="example3" value="#{myFormController.example3}" />
   
   <my:formInputText name="example4" value="#{myFormController.example4}">
    <ui:define name="label"><div>My custom label:</div></ui:define>
   </my:formInputText>
   
  </h:form>
 </ui:define>
</ui:composition>

Dokumentacja
Kiedy komponent/tag jest już gotowy, to warto go opisać. Dokumentacja jak wiadomo jest bardzo ważna, czasami ważniejsza od samego kodu, dlatego należy zadbać o ten punkt niezwykle starannie. Do tej pory nasz komponent jest widoczny przez nasze IDE, ale niestety nie podpowiada nam co możemy z nim zrobić. Aby to się zmieniło wystarczy stworzyć plik TLD (Tag Library Descriptor) w którym starannie opisujemy nasz tag oraz każdy atrybut, jaki w nim występuje. Do opisu możemy używać znaczników html, dzięki czemu nasz opis będzie bardziej przyjemny.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"
                        "http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">
<taglib>
 <tlib-version>1.2</tlib-version>
 <jsp-version>2.0</jsp-version>
 <short-name>mir</short-name>
 <uri>http://mkorwel.blogspot.com/components</uri>
 <display-name>mkorwel</display-name>
 <description>My Components</description>
  
 <tag>
  <name>formInputText</name>
  <tag-class />
  <body-content>empty</body-content>
  <description>Pole formularza renderowane wraz z etykietą.</description>
  <attribute>
      <name>name</name>
         <required>true</required>
         <type>java.lang.String</type>
         <description>Nazwa pola. Na podstawie tego atrybutu generowane jest id komponentów.</description>
  </attribute>
  <attribute>
      <name>value</name>
         <required>true</required>
         <type>java.lang.String</type>
         <description>Wartość pola tekstowego.</description>
  </attribute>
  <attribute>
      <name>labelMsg</name>
         <type>java.lang.String</type>
         <description>Etykieta pola</description>
  </attribute>
 </tag>
</taglib>