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. Damit produzieren Sie alle denkbaren Tags in hoher Geschwindigkeit, auch mit Javascript, AJAX, Styles oder Images.
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!
<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?
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>
<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>
Es soll ein Tag gebaut werden, der an einen Event-Handler hinzugefügt werden kann und den Value eines HTML-Input-Elements verändert. Zusätzlich soll die onchange-Methode des HTML-Input-Elements aufgerufen werden.
Zunächst bauen wir die Methode für das Hinzufügen eines Event-Handlers:
addHandler: function(obj, handler, fn) { var oldFn = obj[handler]; obj[handler] = function(event) { fn(); if (this[handler].oldFn != null) this[handler].oldFn(event); } obj[handler].oldFn = oldFn; }
Diese Funktion ist bereits durch die intersult.js im Intersult-Namespace der verfügbar. Dann den Tag:
<i:meta var="comp" useId="#{id}" javascript="#{intersultResource.get('/intersult.js')}" rendered="#{empty rendered or rendered}"> <script type="text/javascript"> Intersult.addHandler($("#{comp.parentClientId}"), "#{event}", function(event) { $('#{clientId}').value = '#{value}'; if ($('#{clientId}').onchange != null) $('#{clientId}').onchange(); }); </script> </i:meta>
Der Tag kann nun eingesetzt werden, um z.B. bei einer rich:suggestionbox andere Felder mit zu befüllen:
<h:inputText id="patient" autocomplete="off"/> <h:inputText id="gender"/> <h:inputText id="room"/> <h:inputText id="patientId"/> <rich:suggestionbox id="suggest" for="patient" var="suggestion" width="500" height="200" suggestionAction="#{patient.suggestLabel}" fetchValue="#{patient.label}" usingSuggestObjects="true" minChars="3"> <i:setValue event="onselect" clientId="#{comp.namingPrefix}gender" value="#{suggestion.gender}"/> <i:setValue event="onselect" clientId="#{comp.namingPrefix}room" value="#{suggestion.room}"/> <i:setValue event="onselect" clientId="#{comp.namingPrefix}patientId" value="#{suggestion.id}"/> <h:column> <h:outputText value="#{suggestion.label}" escape="false"/> </h:column> </rich:suggestionbox>
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".
<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.
<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.
<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.
Die Intersult Taglib stellt eine Bean auf Applicationebene zur Verfügung, mit der URLs gestreamt werden können:
<h:graphicImage value="#{urlResource.get(some.bean.url)}"/>
Was macht urlResource.get(...)? Die Methode generiert aus einem URL, der nur innerhalb der Infrastruktur zugreifbar ist einen virtuellen URL auf dem Web-Server. Über diesen URL kann später auf den ursprünglichen URL zugegriffen, also auch gestreamt werden. Der Zugriff ist dabei sicher, da von außen keine URLs generisch erzeugt werden können. Nur im Component-Tree gerendete URLs sind gültig.
<component name="transportBundles" class="com.intersult.ui.util.ResourceBundle" scope="application" startup="true"> <property name="bundleNames"> <value>transport-messages</value> </property> </component>
Die Resource-Bundles werden dann sauber zusammengeführt und geladen.
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.