miercuri, 11 februarie 2015

JSF 2.2 - Writing a JSF theme (contract)

Starting with JSF 2.2 we have more control on writing custom themes for JSF. In order to write a basic theme, you have to know a few things, as follows:

1.       Themes are known in JSF as contracts
2.       Contracts are placed in a folder named, contracts under the Web root of the application, or under the META-INF folder that resides in a JAR file
3.       We can alter the location and the name of this folder via WEBAPP_CONTRACTS_DIRECTORY_PARAM_NAME context parameter in web.xml
4.       Commonly, under the contracts folder, we define a subfolder for each contract
5.       The subfolder's name represents the contract's name
6.       A contract contains the contract's artifacts such as the CSS, JS, images, and XHTML templates

If we follow a simple <ui:insert> - <ui:define> construction:

<!-- in template file -->
<ui:insert name="content"/>

<!-- in page that uses the template -->
<ui:define name="content">   
...
</ui:define>

Then we can simply isolate the CSS classes from below - basically, we style the HTML rendered by JSF:

.content {}                                     /* main div */
.content div {}                                 /* <h:panelGroup> */
.content table {}                               /* <h:dataTable>, <h:panelGrid> *>
.content table td,.content table th {}
.content table thead th {}
.content table thead th:first-child {}
.content table tbody td {}
.content table tbody .alt td {}
.content table tbody td:first-child {}
.content table tbody tr:last-child td {}
.content table tfoot td div {}
.content table tfoot td {}
.content table tfoot td ul {}
.content table tfoot li {}
.content table tfoot li a {}
.content table tfoot ul.active,.content table tfoot ul a:hover {}
.content input[type=text] {}                    /* <h:inputText> */
.content input[type=password] {}                /* <h:inputsecret> */
.content input[type=submit] {}                  /* <h:commandButton> */
.content textarea {}                            /* <h:inputTextarea> */
.content label {}                               /* <h:outputLabel> */
.content select {}                              /* <h:selectOneMenu>,
                                                <h:selectOneListbox>,
                                                <h:selectManyMenu>,
                                                <h:selectManyListbox> */
.content input[type=radio] {}                   /* <h:selectOneRadio> */
.content input[type=checkbox] {}                /* <h:selectManyCheckbox> */
.content input:hover {}
.content input:active {}
.content input[type=radio] + label {}
.content input[type=radio]:checked + label {}
.content input[type=checkbox] + label {}
.content input[type=checkbox]:checked + label {}

You can easily add CSS classes for the rest of UI components.

Now, you can provide your custom CSS code and obtain a basic JSF theme.

Per example, let's see the output of a JSF page without using CSS code (of course, you can use some CSS snippets to align things and provide a decent look, but we want a theme component's width and/or height):

The code behind this screenshot is listed below (notice that there is no CSS inline/defined in code below, but you can do it if you need to override someting from the theme (e.g.components width and/or height):

                      <h:panelGrid columns="4">
                        <f:facet name="header">
                            <h:graphicImage library="default" name="images/rafa.png"/>
                        </f:facet>      
                        <h:form enctype="multipart/form-data">
                            <h:panelGrid columns="2">
                                <f:facet name="header">
                                    Upload photos with first three ATP players
                                </f:facet>                               
                                <h:outputLabel for="fileToUploadId" value="Select photos:"/>
                                <h:inputFile id="fileToUploadId" value=""/>       
                                <h:outputLabel for="playerPhotoId" value="Players names:"/>                               
                                <h:selectManyListbox id="playerPhotoId" value="">
                                    <f:selectItem itemValue="RN" itemLabel="RAFAEL NADAL" />
                                    <f:selectItem itemValue="RF" itemLabel="NOVAK DJOKOVIC" />
                                    <f:selectItem itemValue="NJ" itemLabel="DAVID FERRER" />                                  
                                </h:selectManyListbox>
                                <h:outputLabel for="selectId" value="Convert to:"/>                               
                                <h:selectOneMenu id="selectId" value="">
                                    <f:selectItem itemValue="PNG" itemLabel="Portable Network Graphics (PNG)" />
                                    <f:selectItem itemValue="JPG" itemLabel="Joint Photographic Experts Group (JPG)" />
                                    <f:selectItem itemValue="BMP" itemLabel="Bitmap (BMP)" />                                   
                                </h:selectOneMenu>                                                               
                            </h:panelGrid>
                        </h:form>                            
                        <h:form>
                            <h:panelGrid columns="1">
                                <f:facet name="header">
                                    Download your photos
                                </f:facet>                               
                                <h:outputLabel for="sizeId" value="Select size:"/>                               
                                <h:panelGroup id="downloadDivId" layout="block">
                                    <h:selectOneRadio id="sizeId" value="">
                                        <f:selectItem itemValue="16x16" itemLabel="16x16" />
                                        <f:selectItem itemValue="250x250" itemLabel="250x250" />
                                        <f:selectItem itemValue="1280x720" itemLabel="1280x720" />
                                    </h:selectOneRadio>   
                                </h:panelGroup>
                                <h:commandButton value="Download"/>
                            </h:panelGrid>
                        </h:form>
                        <h:form>                       
                            <h:panelGrid columns="2">
                                <f:facet name="header">
                                    Log in
                                </f:facet>
                                <h:outputLabel for="emailId" value="E-mail:"/>
                                <h:inputText value=""/>
                                <h:outputLabel for="passwordId" value="Password:"/>
                                <h:inputSecret value=""/>                               
                            </h:panelGrid>
                        </h:form>
                        <h:form>
                            <h:panelGrid columns="2">
                                <f:facet name="header">
                                    Contact Us
                                </f:facet>
                                <h:outputLabel for="subjectId" value="Subject:"/>
                                <h:inputText id="subjectId" value=""/>
                                <h:outputLabel for="messId" value="Message:"/>
                                <h:inputTextarea id="messId" cols="100"/>
                                <f:verbatim/>
                                <h:commandButton value="Send"/>
                            </h:panelGrid>
                        </h:form>
                    </h:panelGrid> 

                    <h:panelGrid columns="1">
                        <f:facet name="header">
                            ATP Singles Today
                        </f:facet>
                        <h:dataTable value="#{playersBean.data}" var="t" border="1">
                            <h:column>
                                <f:facet name="header">
                                    Ranking
                                </f:facet>
                                #{t.ranking}
                            </h:column>
                            <h:column>
                                <f:facet name="header">
                                    Name
                                </f:facet>
                                #{t.player}
                            </h:column>
                            <h:column>
                                <f:facet name="header">
                                    Age
                                </f:facet>
                                #{t.age}
                            </h:column>
                            <h:column>
                                <f:facet name="header">
                                    Birthplace
                                </f:facet>
                                #{t.birthplace}
                            </h:column>
                            <h:column>
                                <f:facet name="header">
                                    Residence
                                </f:facet>
                                #{t.residence}
                            </h:column>
                            <h:column>
                                <f:facet name="header">
                                    Height (cm)
                                </f:facet>
                                #{t.height}
                            </h:column>
                            <h:column>
                                <f:facet name="header">
                                    Weight (kg)
                                </f:facet>
                                #{t.weight}
                            </h:column>
                            <h:column>
                                <f:facet name="header">
                                    Coach
                                </f:facet>
                                #{t.coach}
                            </h:column>
                            <h:column>
                                <f:facet name="header">
                                    Born
                                </f:facet>
                                <h:outputText value="#{t.born}">               
                                    <f:convertDateTime pattern="dd.MM.yyyy" />
                                </h:outputText>
                            </h:column>
                            <f:facet name="footer">                           
                                <div id="paging">
                                    <ul>
                                        <li>
                                            <a href="#">
                                                <span>Previous</span>
                                            </a>
                                        </li>
                                        <li>
                                            <a href="#" class="active">
                                                <span>1</span>
                                            </a>
                                        </li>
                                        <li>
                                            <a href="#">
                                                <span>2</span>
                                            </a>
                                        </li>
                                        <li>
                                            <a href="#">
                                                <span>3</span>
                                            </a>
                                        </li>
                                        <li>
                                            <a href="#">
                                                <span>4</span>
                                            </a>
                                        </li>
                                        <li>
                                            <a href="#">
                                                <span>5</span>
                                            </a>
                                        </li>
                                        <li>
                                            <a href="#">
                                                <span>Next</span>
                                            </a>
                                        </li>
                                    </ul>
                                </div>
                            </f:facet>
                        </h:dataTable>
                    </h:panelGrid>

Now, we have populate the CSS classes listed earlier in a file arbitrary named, styles.css. This CSS file and a template file are added under a folder named, jsftheme. This is the JSF contract. Now, we can use it like this:

1.       load the styles.css in template file (template.xhtml)

<h:body>
 <!-- the styles.css contains the theme CSS -->
 <h:outputStylesheet name="styles.css"/>       
  <div class="content">
   <ui:insert name="content"/>
  </div>      
</h:body>

2.       in the main page (index.xhtml) use the template (template.xhtml) and indicate the theme (jsftheme)

<h:body>
 <f:view contracts="jsftheme">
  <ui:composition template="/template.xhtml">                                 
    <ui:define name="content">   
     ... <!-- the JSF page code listed earlier -->
    </ui:define>              
  </ui:composition>                       
</f:view>
</h:body>

The result is in figure below:
So, now you know how to write a simple theme.

The complete code is on GitHub [StylingJSF]

Niciun comentariu:

Trimiteți un comentariu