Die Intersult Taglib ist eine Taglib zur Produktion von Taglibs. Die produzierten Tags sind geeignet für die Entwicklung von JEE-Applikationen unter JSF, Seam, Facelets, Tomahawk und Richfaces. Während andere Unternehmen ganze Mannschaften beauftragen, produzieren Sie alle denkbaren Tags in extrem hoher Geschwindigkeit. Egal ob Javascript, AJAX, Styles oder Images. !!!Tag "meta" Der wichtigste Tag von "com.intersult.ui" ist der meta-Tag. Was ist ein Meta-Tag? Das ist ein Tag zur Konstruktion einer Component mittels einer XHTML-Datei mit vollwertigem Id- und Rendering-Verhalten. Der Bau eigener Components wird damit zum Kinderspiel, da in der Regel kein Java-Code mehr geschrieben werden braucht. Dabei sind Features enthalten, wie Component-Id-Generierung, JSF-Client-Id-Access aus Javascript, Javascript- und CSS-Header-Insertions, Resource-Exposition aus dem JAR, Text-Interpolation (Evaluierung von EL-Expressions) in allen Text-Resources etc. Das Gießen eigener Taglibs ist noch nie so einfach gewesen wie heute! !Attribute Der meta-Tag unterstützt einige Attribute: * __var:__ Legt den Variablennamen fest, auf dem die Component-Informationen gebunden werden. Der meta-Tag generiert eine Component-Id und stellt diese unter der unter ''var'' angegebenen Variable (hier ''comp'') zur Verfügung. Diese Variable hat zwei Properties: ** __id__ Innerhalb des meta-Tags kann hier eine Id abgerufen werden und als Zuweisung für eine andere Komponente verwendet werden. Diese Id darf natürlich nur einmal verwendet werden, da die im Component-Tree von JSF eindeutig sein muss. Werden mehrere Ids gebraucht, können diese mit #{comp.id}-suffix erzeugt werden. ** __clientId__ Innerhalb des meta-Tags kann hier die clientId, als die im HTML-Code tatsächlich heraus generierte Id abgefragt werden. Dies wird vom JavaScript-Code gebraucht. * __useId:__ Der meta-Tag generiert eine Id mit dem JSF-Verfahren, falls diese nicht mit useId überschrieben wird. Im Gegensatz zum Zuweisen normaler Ids darf useId="#{id}" benutzt werden, auch wenn die Variable 'id' nicht zugewiesen oder einen Leerstring enthält. Die so gebaute Component arbeitet dann dennoch korrekt mit einer generierten Id. * __rendered:__ Gibt an ob die Component gerendered wird oder nicht. * __stylesheet:__ Ein hier angegebener Pfad wird in den Header der HTML-Seite als Link zu einem Stylesheet integriert. Der Link wird dabei nur einmal integriert, auch wenn die Komponente mehrfach auf einer Page verwendet wird. Dies ist vor allem im Zusammenhang mit einer ClassPathResource nützlich. * __javascript:__ Analog zum Attribut stylesheet wird hier eine Javascript-Datei per Link in den Header der Page eingebunden. Dieser Link wird ebenfalls nur einmal erzeugt und nützlicher Weise mit einer ClassPathResource verknüpft. * __index:__ Liefert die Position des eigenen Tags im Parent Tag. Damit lassen sich Tabellen einfach mit gestreiftem Muster versehen, erste und letzte Zeile mit besonderem Style versehen etc. * __count:__ Liefert die Gesamtzahl der Tags im Parent Tag. * __first:__ Liefert einen boolean-Wert ob der Tag der erste Tag im Parent Tag ist. Damit lassen sich z.B. Header einfärben oder mit anderen Images versehen. * __last:__ Wie first, nur dass hier auf den letzten Tag geprüft wird. !Beispiel Infobox Als Aufgabenstellung soll ein Facelet gebaut werden, das auf einer XHTML-Seite wie folgt eingebaut werden kann: {{{ <i:info id="info" value="Dies ist die Infobox"/> }}} Die Implementierung erfolgt als Facelet in der Datei info.xhtml: {{{ <ui:composition> <i:meta useId="#{id}" var="comp" rendered="#{empty rendered or rendered}"> <s:graphicImage id="#{comp.id}" url="#{empty img ? '/images/information.png' : img}" onmouseover="$('#{comp.clientId}-text').style.display = '';" onmouseout="$('#{comp.clientId}-text').style.display = 'none';"/> <s:span id="#{comp.id}-text" style="display: none; position: fixed; background-color: yellow; padding: 3px;"> <h:outputText value="#{value}"/> </s:span> </i:meta> </ui:composition> }}} Die *.taglib.xml enthält folgenden Eintrag, um das Facelet zu registrieren: {{{ <tag> <tag-name>info</tag-name> <source>/tag/info.xhtml</source> </tag> }}} Was kann dieser Tag? * __Id-Parameter:__ Die Id der Facelet-Component verhält sich identisch zu einem in Java entwickelten Tag. Wird keine Id angegeben, wird über den JSF-Mechanismus eine Id generiert. Optional kann eine Id angegeben werden, diese wird dann auch verwendet. * __clientId:__ Es steht immer die clientId zur Verfügung, sodass beliebiger Code in JavaScript hinzugefügt werden kann. Dieser Code kann ohne Konflikte an mehreren Stellen auf einer XHTML-Seite included werden kann. * __rendered-Attribut:__ Das Attribut ''rendered'' ist etwas tricky, da die Component sowohl gerendered werden muss, wenn rendered nicht angegeben wurde (Prüfung mit empty) als auch wenn rendered="true" angegeben wurde. * __img-Attribut:__ Das Attribut ''img'' ist ebenfalls optional. Der ?-:-Operator prüft, ob das img angegeben wurde, wenn nicht wird als Default-Wert '/images/information.png' eingesetzt. Andernfalls wird der angegebene Wert verwendet. Als Luxus kann noch eine TLD im Verzeichnis META-INF abgelegt werden, sodass z.B. der Content Assist von [Eclipse] eine Code Completion für die eben gebaute Component vornehmen kann: {{{ <tag> <name>info</name> <tag-class/> <body-content>empty</body-content> <description>Renderes a info icon with popup div.</description> <attribute> <name>id</name> <required>false</required> <rtexprvalue>false</rtexprvalue> <type>java.lang.String</type> </attribute> <attribute> <name>value</name> <required>true</required> <rtexprvalue>false</rtexprvalue> <type>java.lang.String</type> <description>Text for popup.</description> </attribute> <attribute> <name>img</name> <required>false</required> <rtexprvalue>false</rtexprvalue> <type>java.lang.String</type> <description>Optional: Image for the info icon.</description> </attribute> <attribute> <name>rendered</name> <description> Flag indicating whether this component (and its children) should be rendered. Expressions must evaluate to a boolean. </description> </attribute> </tag> }}} Einschränkungen: Der meta-Tag ist nur mit State-Saving auf dem Server getest, d.h. ohne Serialisierung. Wenn es Probleme gibt, in der web.xml folgende Parameter setzen: {{{ <context-param> <param-name>javax.faces.STATE_SAVING_METHOD</param-name> <param-value>server</param-value> </context-param> }}} !Beispiel Menü Eine eigene Menükomponente wird durch die Tage menu und menuItem gebaut. Zunächst wird eine Resource in der components.xml definiert: {{{ <component name="resource" class="com.intersult.ui.util.ClassPathResource" scope="application"> <property name="resourcePath">/test</property> <property name="classPath">/de/test/ui/resource</property> </component> }}} Dann der Tage "menu": {{{ <ui:composition> <i:meta var="comp" useId="#{id}" rendered="#{empty rendered or rendered}" stylesheet="#{resource.get('/test.css')}"> <table id="#{comp.clientId}" cellpadding="0" cellspacing="0"> <i:clear var="id,rendered"> <ui:insert/> </i:clear> </table> </i:meta> </ui:composition> }}} Und schließlich der Tag "menuItem": {{{ <ui:composition> <i:meta var="itemComp" useId="#{id}" rendered="#{empty rendered or rendered}" stylesheet="#{resource.get('/test.css')}"> <tr> <td class="#{itemComp.last ? 'test-menuL' : 'test-menuI'}"/> <td class="test-menuContent"> <i:clear var="id,rendered"> <ui:insert/> </i:clear> </td> </tr> </i:meta> </ui:composition> }}} Schließlich wird das Ganze wieder in die test.taglib.xml eingetragen, und in die test.tld, sodass der Content-Assist in der Entwicklungsumgebung den Tag auch anzeigen kann. Die die vorkommenden Styles in die test.css eingetragen. Mit diesen Tags können dann einfach eigene Menüs inklusive beliebig verschachtelter Untermenüs gebaut werden: {{{ <o:menu> <o:menuItem> <s:link value="#{msg['menu1']}" action="menu1" propagation="none"/> </o:menuItem> <o:menuItem> <s:link value="#{msg['menu2']}" action="menu2"/> <o:menu> <o:menuItem> <s:link value="#{msg['menu2.1']}" action="home" propagation="none"/> </o:menuItem> </o:menu> </o:menuItem> </o:menu> }}} !!!Tag "clear" Bei verschachtelter Anwendung des meta-Tags werden Facelet-Parameter unkontrolliert in innere Facelets weiter gegeben. Insbesondere gefährdet ist ui:insert, da hier Facelets generisch eingefügt werden. Dies findet bei ui:include und ui:param ebenso statt, wie bei der Definition von Tags in der Facelets-Taglib. Um dies zu verhindern, steht der Intersult clear-Tag zur Verfügung: {{{ <i:clear var="id,label"> <ui:insert/> </i:clear> }}} Was geschieht hier? Der clear-Tag verhindert die weitere Propagierung der UIParameter (also auch Tag-Attribute) "id" und "label". !Beispiel inputGroup Das Beispiel soll den Nutzen des clear-Tags verdeutlichen. Zunächst wird ein Tag eingesetzt, der die Gruppierung von Eingabefeldern vornimmt: {{{ <i:inputGroup label="Gruppe"> <i:inputText id="test" value="#{test}"/> </i:inputGroup> }}} Die Implementierung der inputGroup ist wie folgt: {{{ <i:meta var="comp" useId="#{id}" rendered="#{empty rendered or rendered}"> <fieldset id="#{comp.clientId}"> <legend> <h:outputText value="#{label}"/> </legend> <i:clear var="id,label"> <ui:insert/> </i:clear> </fieldset> </i:meta> }}} Die Implementkerung von inputText: {{{ <i:meta var="comp" useId="#{id}" rendered="#{empty rendered or rendered}"> <h:panelGrid> <h:inputLabel for="#{comp.id"} value="#{empty label ? 'Default' : label}"/> <h:inputText id="#{comp.id}" value="#{value}"/> <i:message id="#{comp.id}-message" for="#{comp.id}"/> </h:panelGrid> </i:meta> }}} Der Tag inputText enthält wieder eine Referenz auf die Variable Label. Ohne Verwendung des clear-Tag wäre diese nun auf den äußeren Wert gesetzt, da der inputGroup-Tag den Wert nach innen hinein propagiert. !!!Tag "inputHidden" Dieser Tag ist eine Erweiterung des inputHidden aus der Tomahawk Bibliothek um das Property "from". Selbst in Seam-Projekten werden Informationen aus der Oberfläche, insbesondere bei Listen, als URL-Parameter übergeben. Dies unterwandert das Konzept der Komponenten, da der URL-Parameter dann auf Page-Ebene behandelt wird. Dafür muss zusätzlicher Code geschrieben werden, vermindert die Wartbarkeit und erhöht die Fehleranfälligkeit durch Inkonsistenzen zwischen URL-Parameter und Code-Verarbeitung. Des Weiteren widerstrebt es dem Konzept von meaningful URL und läd zur Manipulation ein. !Beispiel Folgendes Beispiel zeigt den Einsatz des inputHidden-Tags für ein Steuerelement eines Paging-Mechanismus einer Liste. Es ist ein Mini-Formular mit einem commandLink und dem inputHidden, das den gewünschten Index direkt auf die Liste schreibt. {{{ <h:form> <h:commandLink action="redirect">#{index}</h:commandLink> <i:inputHidden value="#{list.firstResult}" from="#{index}"/> </h:form> }}} Statt dem h:commandLink kann natürlich auch ein a4j:commandLink benutzt werden. !!!ClassPathResource Mit dieser Klasse kann ein Resource-Path direkt aus dem ClassLoader in die Web-Applikation exponiert werden. Eine JAR-Datei die sich in einem WAR befindet, hat zunächst keine Möglichkeit auf einen URL zu mappen. Innerhalb eines Facelets aus einer Taglib können zwar URLs erzeugt werden mit h:graphicImage, h:outputLink etc., jedoch keine Dateien als Linkziel exponiert. Die Klasse ClassPathResource exponiert einen Resource-Path mittels eines Eintrag in die components.xml: {{{ <component name="taglibResource" class="com.intersult.ui.util.ClassPathResource" scope="application"> <property name="resourcePath">/taglib</property> <property name="classPath">/resource</property> </component> }}} Die Resourcen sind dann zugreifbar mit: {{{ <t:stylesheet path="#{taglibResource.get('/style.css')}"/> }}} Alle Resources mit Mime-Type text/* werden dabei interpoliert. Insbesondere CSS-Stylesheets und Javascript-Dateien die durch den meta-Tag geladen werden, können dabei mit EL-Expressions versehen werden. Dies ist z.B. interessant beim Styles vom Typ "background-image", da hier über "url(...)" auf eine Resource zugegriffen wird. !!!Installation !Maven2 Integration Läuft der Build-Prozess in Ihrem Projekt bereits auf [Maven2|http://maven.apache.org/]? Wenn nicht, sollten Sie es sich gründlich überlegen. Der Aufwand zum Einlernen ist sehr gering und der Benefit beim Dependency, Build, Deployment, Distribution, Source Code, Development Environment und andere Prozesse profitieren wesentlich. Um die Taglib einzubinden, brauchen Sie nur folgende Einträge in Ihrem pom.xml vorzunehmen: {{{ <project> ... <dependencies> ... <dependency> <groupId>com.intersult</groupId> <artifactId>com.intersult.ui</artifactId> <version>1.0-SNAPSHOT</version> </dependency> ... </dependencies> ... <repositories> ... <repository> <id>intersult-repository</id> <name>Intersult Repository</name> <url>http://repository.intersult.com/repository</url> </repository> ... </repositories> ... </project> }}} Bei dem Arbeiten mit Eclipse, ist die Taglib nach einem "mvn eclipse:eclipse" und einem Refresh des Projekts verfügbar. Ihnen ist unklar, wie der Content-Assist unter Eclipse für Tags korrekt konfiguriert wird? Wir beantworten gerne diese und weiter Fragen. Nehmen Sie Kontakt mit uns auf. !Manuell Folgende Bibliotheken sind im Stand vom 07.06.2009. Daher sind nicht alle oben beschriebenen Features enthalten. Es wird dringend der Einsatz von Maven2 empfohlen. * [Intersult Taglib/com.intersult.ui-1.0-20090607.204442-8.jar] * [Intersult Taglib/com.intersult.util-1.0-20090607.204442-7.jar] * JBoss Seam 2.1.2.CR2 * Richfaces 3.3.0.GA * Facelets 1.1.15.B1 * JSF API/IMPL 1.2_12 * Tomahawk 1.1.8 * Servlet API 2.5 * JSP API 2.0 * Apache commons-lang 2.1 * Apache commons-collections 3.2 * Apache commons-beanutils 1.6