Dear Wiki user, You have subscribed to a wiki page or wiki category on "Shale Wiki" for change notification.
The following page has been changed by Hermod Opstvedt: http://wiki.apache.org/shale/CreatingClayComponents ------------------------------------------------------------------------------ If you started with the Maven2 archetype, the person bean will already have been defined as a managed bean in the faces-config.xml file, if not enter this into it: {{{ - <managed-bean id="person">[[BR]] + <managed-bean id="person"> - <managed-bean-name>person</managed-bean-name>[[BR]] + <managed-bean-name>person</managed-bean-name> - <managed-bean-class>[[BR]] + <managed-bean-class> - com.acme.test.Person[[BR]] + com.acme.test.Person - </managed-bean-class>[[BR]] + </managed-bean-class> - <managed-bean-scope>session</managed-bean-scope>[[BR]] + <managed-bean-scope>session</managed-bean-scope> - </managed-bean>[[BR]] + </managed-bean> }}} Now we almost have the receiver for the data in place. What we lack is a viewcontroller (controller in MVC) for the person registration form. Create a bean PersonVC that extends Abstract``View``Controller and has one attribute of type Person with respective getter/setter. @@ -48, +48 @@ Define the following in faces-config.xml file {{{ - <managed-bean id="personReg">[[BR]] + <managed-bean id="personReg">] - <managed-bean-name>personReg</managed-bean-name>[[BR]] + <managed-bean-name>personReg</managed-bean-name>] - <managed-bean-class>[[BR]] + <managed-bean-class>] - com.acme.test.PersonVC[[BR]] + com.acme.test.PersonVC] - </managed-bean-class>[[BR]] + </managed-bean-class>] - <managed-bean-scope>request</managed-bean-scope>[[BR]] + <managed-bean-scope>request</managed-bean-scope>] - <managed-property>[[BR]] + <managed-property>] - <property-name>person</property-name>[[BR]] + <property-name>person</property-name>] - <property-class>[[BR]] + <property-class>] - com.acme.test.Person[[BR]] + com.acme.test.Person] - </property-class>[[BR]] + </property-class>] - <value>#{person}</value>[[BR]] + <value>#{person}</value>] - </managed-property>[[BR]] + </managed-property>] - </managed-bean>[[BR]] + </managed-bean>] }}} The next we do is to add a method that "saves" the person. Do this by adding the following method: {{{ - public String save()[[BR]] + public String save() - {[[BR]] + { - return null;[[BR]] + return null; - }[[BR]] + } }}} For now this method does nothing. Also note that it returns null. This means that no navigation rule will be executed and we remain at the same form. @@ -84, +84 @@ We start by creating the generic labell/input filed component and we will call it "gltic". This is short for Generic Label Text Input Component. {{{ - <component jsfid="gltic" extends="clay" id="gltic">[[BR]] + <component jsfid="gltic" extends="clay" id="gltic">] - <element jsfid="outputLabel" renderId="1">[[BR]] + <element jsfid="outputLabel" renderId="1">] - <attributes>[[BR]] + <attributes>] - <set name="value" value="@ltName"></set>[[BR]] + <set name="value" value="@ltName"></set>] - </attributes>[[BR]] + </attributes>] - </element>[[BR]] + </element>] - <element jsfid="inputText" renderId="2">[[BR]] + <element jsfid="inputText" renderId="2">] - <attributes>[[BR]] + <attributes>] - <set name="value" value="@ltValue"></set>[[BR]] + <set name="value" value="@ltValue"></set>] - </attributes>[[BR]] + </attributes>] - </element>[[BR]] + </element>] - </component>[[BR]] + </component>] }}} There are a couple of things to note here. Firstsly, we extend from "clay". All components must either extend "clay" or another existing component. Next thing to note is that within the component define elements that are part of the component. Be careful with the rendered attribute. This attribute governs the rendering order of the elements to the device. So be careful to order them in the order that you want them displayed. Also take care that the renderIDs are unique at the same element level (You can have nested elements). @@ -105, +105 @@ Lets now try to define the first panel, which is the name panel. As we said earlier there are a lot of ready-made component libraries for us to choose from. We are now going to use one of these. It is know as [http://myfaces.apache.org/tomahawk/index.html/ Tomahawk] and comes from the [http://myfaces.apache.org/ MyFaces project]. The component that we are going to use is the [http://myfaces.apache.org/tomahawk/htmlTag.html/ htmlTag]. This is a component that will render any HTML tag that you specify as a value. To be able to use this, you need to download it and install it into you project. How you do this depends on whether you are using the Maven2 plugin, Maven2 by it self or not at all. The important thing is that you get the tomahawk jar file in the WEB-INF/LIB when you deploy your application(if itâs a standalone WAR). We base our application on the latest version which is currently 1.5-SNAPSHOT. Next is to make Clay aware of the components. You do that by adding th e attachment:tomahawk-1.1.5-SNAPSHOT-config.xml Clay configuration file to your WEB-INF folder. Then change the following section in the web.xml file: {{{ - <!-- Clay Common Configuration Resources -->[[BR]] + <!-- Clay Common Configuration Resources -->] - <context-param>[[BR]] + <context-param>] - <param-name>[[BR]] + <param-name>] - org.apache.shale.clay.COMMON_CONFIG_FILES[[BR]] + org.apache.shale.clay.COMMON_CONFIG_FILES] - </param-name>[[BR]] + </param-name>] - <param-value>[[BR]] + <param-value>] - /WEB-INF/clay-config.xml,[[BR]] + /WEB-INF/clay-config.xml,] - /WEB-INF/tomahawk-1.1.5-SNAPSHOT-config.xml[[BR]] + /WEB-INF/tomahawk-1.1.5-SNAPSHOT-config.xml] - </param-value>[[BR]] + </param-value>] - </context-param>[[BR]] + </context-param>] }}} We are now ready to use all the components that Tomahawk has to offer. @@ -122, +122 @@ Back to our name panel, there is one thing that we need to decide. What to use as the outermost container? As you may know JSF builds a component tree and uses POST to send data from the client to the server and that requires a form. Since we are going to add more components to our page that carry data, we choose to use a tag that is frequently (container) used within a form: Fieldset. So this will be our container. Now our componet looks likes this: {{{ - <component jsfid="namepanel" extends="clay" id="namepanel">[[BR]] + <component jsfid="namepanel" extends="clay" id="namepanel">] - <element jsfid="t:htmlTag" renderId="1" id="namefieldset">[[BR]] + <element jsfid="t:htmlTag" renderId="1" id="namefieldset">] - <attributes>[[BR]] + <attributes>] - <set name="value" value="fieldset" />[[BR]] + <set name="value" value="fieldset" />] - </attributes>[[BR]] + </attributes>] - <element jsfid="t:htmlTag" renderId="1" id="namelegend">[[BR]] + <element jsfid="t:htmlTag" renderId="1" id="namelegend">] - <attributes>[[BR]] + <attributes>] - <set name="value" value="legend" />[[BR]] + <set name="value" value="legend" />] - </attributes>[[BR]] + </attributes>] - <element jsfid="outputText" renderId="1">[[BR]] + <element jsfid="outputText" renderId="1">] - <attributes>[[BR]] + <attributes>] - <set name="value"[[BR]] + <set name="value"] - value="#{messages['namepanel.text']}">[[BR]] + value="#{messages['namepanel.text']}">] - </set>[[BR]] + </set>] - </attributes>[[BR]] + </attributes>] - </element>[[BR]] + </element>] - </element>[[BR]] + </element>] - <element jsfid="gltic" renderId="2" id="firstname">[[BR]] + <element jsfid="gltic" renderId="2" id="firstname">] - <symbols>[[BR]] + <symbols>] - <set name="ltName"[[BR]] + <set name="ltName"] - value="#{messages['firstname.label']}">[[BR]] + value="#{messages['firstname.label']}">] - </set>[[BR]] + </set>] - <set name="ltValue"[[BR]] + <set name="ltValue"] - value="[EMAIL PROTECTED]'firstname']}">[[BR]] + value="[EMAIL PROTECTED]'firstname']}">] - </set>[[BR]] + </set>] - </symbols>[[BR]] + </symbols>] - </element>[[BR]] + </element>] - <element jsfid="t:htmlTag" renderId="3" id="navnbr">[[BR]] + <element jsfid="t:htmlTag" renderId="3" id="navnbr">] - <attributes>[[BR]] + <attributes>] - <set name="value" value="br" />[[BR]] + <set name="value" value="br" />] - </attributes>[[BR]] + </attributes>] - </element>[[BR]] + </element>] - <element jsfid="gltic" renderId="4" id="lastname">[[BR]] + <element jsfid="gltic" renderId="4" id="lastname">] - <symbols>[[BR]] + <symbols>] - <set name="ltName"[[BR]] + <set name="ltName"] - value="#{messages['lastname.label']}">[[BR]] + value="#{messages['lastname.label']}">] - </set>[[BR]] + </set>] - <set name="ltValue"[[BR]] + <set name="ltValue"] - value="[EMAIL PROTECTED]'lastname']}">[[BR]] + value="[EMAIL PROTECTED]'lastname']}">] - </set>[[BR]] + </set>] - </symbols>[[BR]] + </symbols>] - </element>[[BR]] + </element>] - </element>[[BR]] + </element>] - </component>[[BR]] + </component>] }}} @@ -178, +178 @@ We now have a panel where we can enter firstname and lastname. But we also need a panel to enter the address information. As we mentioned earlier this time we define the component in HTML. The principals are the same, only now we are using HTML and are able to do preview on what we do. The panel then becomes (stripped): {{{ - <fieldset>[[BR]] + <fieldset>] - <legend>[[BR]] + <legend>] - <span jsfid="outputText" value="@adresslegend" allowbody="false">Adress</span>[[BR]] + <span jsfid="outputText" value="@adresslegend" allowbody="false">Adress</span>] - </legend>[[BR]] + </legend>] - <label jsfid="outputLabel" value="@streetadressLabel" allowbody="false">[[BR]] + <label jsfid="outputLabel" value="@streetadressLabel" allowbody="false">] - Streetadress[[BR]] + Streetadress] - </label>[[BR]] + </label>] - <input jsfid="inputText" value="@streetadressField" type="text" allowbody="false">[[BR]] + <input jsfid="inputText" value="@streetadressField" type="text" allowbody="false">] - <br>[[BR]] + <br>] - <label jsfid="outputLabel" value="@zipcodeLabel" allowbody="false">[[BR]] + <label jsfid="outputLabel" value="@zipcodeLabel" allowbody="false">] - Zipcode[[BR]] + Zipcode] - </label>[[BR]] + </label>] - <input jsfid="inputText" value="@zipcodeField" type="text" allowbody="false">[[BR]] + <input jsfid="inputText" value="@zipcodeField" type="text" allowbody="false">] - <br>[[BR]] + <br>] - <label jsfid="outputLabel" value="@cityLabel" allowbody="false">[[BR]] + <label jsfid="outputLabel" value="@cityLabel" allowbody="false">] - City[[BR]] + City] - </label>[[BR]] + </label>] - <input jsfid="inputText" value="@cityField" type="text" allowbody="false">[[BR]] + <input jsfid="inputText" value="@cityField" type="text" allowbody="false">] - </fieldset>[[BR]] + </fieldset>] }}} As you can see this is pure HTML with some special attributes on some of the HTML tags. These attributes are Clay directives. The attribute "jsfid" points at a Clay component defined in a Clay configuration file. Next is "value" where we which value the tag should have. Since this is to be a reusable component, we use the symbol instead of hardcoded values. The "SPAN" tag is used as a replacement for the cases where we want to simply output some text that is not related to any tag. We save this in the pages folder and give it a name of adresspanel.html. @@ -204, +204 @@ Now that we have defined our HTML based Clay component, we need to tell Clay of its existence. We do that by adding the following in our clay-config.xml file; {{{ - <component jsfid="adresspanel" extends="clay" id="adresspanel">[[BR]] + <component jsfid="adresspanel" extends="clay" id="adresspanel">] - <attributes>[[BR]] + <attributes>] - <set name="clayJsfid" value="/pages/adresspanel.html" />[[BR]] + <set name="clayJsfid" value="/pages/adresspanel.html" />] - </attributes>[[BR]] + </attributes>] - </component>[[BR]] + </component>] }}} Note that we here use a special attribute "clayJsfid". This is Clay's variant of including something (ala jsp:include). So here we import our HTML component and assign it a Clay id (jsfid). @@ -234, +234 @@ SO lets stitch this together as a whole. Since we are not adding any more components to our page, we can use the form as basis for it. Our completed component then becomes: {{{ - <component jsfid="personregpanel" extends="form"[[BR]] + <component jsfid="personregpanel" extends="form"] - id="personregpanel">[[BR]] + id="personregpanel">] - <element jsfid="namepanel" renderId="1"></element>[[BR]] + <element jsfid="namepanel" renderId="1"></element>] - <element jsfid="t:htmlTag" renderId="2" id="pbr">[[BR]] + <element jsfid="t:htmlTag" renderId="2" id="pbr">] - <attributes>[[BR]] + <attributes>] - <set name="value" value="br" />[[BR]] + <set name="value" value="br" />] - </attributes>[[BR]] + </attributes>] - </element>[[BR]] + </element>] - <element jsfid="adresspanel" renderId="3">[[BR]] + <element jsfid="adresspanel" renderId="3">] - <symbols>[[BR]] + <symbols>] - <set name="adresslegend" value="#{messages['adresspanel.text']}"></set>[[BR]] + <set name="adresslegend" value="#{messages['adresspanel.text']}"></set>] - <set name="streetadressLabel" value="#{messages['streetadress.label']}"></set>[[BR]] + <set name="streetadressLabel" value="#{messages['streetadress.label']}"></set>] - <set name="streetadressFeield" value="[EMAIL PROTECTED]'streetadress']}"></set>[[BR]] + <set name="streetadressFeield" value="[EMAIL PROTECTED]'streetadress']}"></set>] - <set name="zipcodeLabel" value="#{messages['zipcode.label']}"></set>[[BR]] + <set name="zipcodeLabel" value="#{messages['zipcode.label']}"></set>] - <set name="zipcodeFelt" value="[EMAIL PROTECTED]'zipcode']}"></set>[[BR]] + <set name="zipcodeFelt" value="[EMAIL PROTECTED]'zipcode']}"></set>] - <set name="cityLabel" value="#{messages['city.label']}"></set>[[BR]] + <set name="cityLabel" value="#{messages['city.label']}"></set>] - <set name="cityField" value="[EMAIL PROTECTED]'city']}"></set>[[BR]] + <set name="cityField" value="[EMAIL PROTECTED]'city']}"></set>] - </symbols>[[BR]] + </symbols>] - </element>[[BR]] + </element>] - <element jsfid="t:htmlTag" renderId="4" id="pbr2">[[BR]] + <element jsfid="t:htmlTag" renderId="4" id="pbr2">] - <attributes>[[BR]] + <attributes>] - <set name="value" value="br" />[[BR]] + <set name="value" value="br" />] - </attributes>[[BR]] + </attributes>] - </element>[[BR]] + </element>] - <element jsfid="saveButton" renderId="5">[[BR]] + <element jsfid="saveButton" renderId="5">] - <symbols>[[BR]] + <symbols>] - <set name="metode" value="save"></set>[[BR]] + <set name="metode" value="save"></set>] - </symbols>[[BR]] + </symbols>] - </element>[[BR]] + </element>] - </component>[[BR]] + </component>] }}} Here we have defined our reusable components as elements (respecting the rendered) and defined actual values to substitute the symbols with. @@ -284, +284 @@ To refine this you can substitute the zipcode field with a combobox that gets its values from the Post beans getZipcodes method (This is shown in the downloadable finished application). The HTML definition for it is: {{{ - <select value="@zipcodeFelt" allowBody="true">[[BR]] + <select value="@zipcodeFelt" allowBody="true"> - <option value="@zipcodesList" />[[BR]] + <option value="@zipcodesList" /> - </select>[[BR]] + </select> }}} In addition we also have to declare what the symbol "zipcodesList" is to be replaced with when we use it on our page. @@ -295, +295 @@ [http://java.sun.com/javaee/javaserverfaces/1.1_01/docs/api/javax/faces/model/SelectItem.html/ SelectItem] '''Select''Item[]''', Collection or Map. We use Select``Item, so you need to add the following method to the Post bean: {{{ - private void initZipcodesList() {[[BR]] + private void initZipcodesList() {] - zipcodeliste=new Select``Item[zipcodes.length];[[BR]] + zipcodeliste=new Select``Item[zipcodes.length];] - Select``Item selectItem;[[BR]] + Select``Item selectItem;] - for(int i=0; i < zipcodes.length; i++)[[BR]] + for(int i=0; i < zipcodes.length; i++)] - {[[BR]] + {] - selectItem=new Select``Item();[[BR]] + selectItem=new Select``Item();] - selectItem.setLabel(zipcodes[i].toString());[[BR]] + selectItem.setLabel(zipcodes[i].toString());] - selectItem.setValue(zipcodes[i]);[[BR]] + selectItem.setValue(zipcodes[i]);] - zipcodesliste[i]=selectItem;[[BR]] + zipcodesliste[i]=selectItem;] - }[[BR]] + }] - [[BR]] + ] - }[[BR]] + }] {{{ And add a default constructor to it: {{{ - public Post()[[BR]] + public Post() - {[[BR]] + { - initZipcodesList();[[BR]] + initZipcodesList(); - }[[BR]] + } }}} When you run this it will look like this:
