Blog Archives

November 30th, 2010
4:37 pm
Tailoring a Primefaces TreeTable with JavaScript/jQuery/JSF 2.0

Posted under JSF
Tags , , , , , , , ,

This example came about from the desire to hightlight a Primefaces TreeTable row when a checkbox in the row is ticked. I wanted to do this because a ticked checkbox is not as visible as I would like when a lot of data is present. I was not keen on adding DataTable style dynamic selection by hovering and clicking anywhere in a row, as this conflicts somewhat with the paradigm of clicking on the expander to expand/collapse the tree, and you would have to special case hovering/clicking on the expander somehow to prevent row selection.

Ideally you would use CSS for this, but this would involve tagging a component in a TreeTable cell (<td> element) with a class (as in the TreeTable component we cannot directly add row styling), and then using some kind of ancestor selector to traverse the DOM tree upwards to get the row. The simple answer to this is that whilst CSS can do some clever things, it does not have any kind of ancestor selector, so it is not possible to do it just with CSS.

An attractive alternative for this kind of thing in Primefaces is to use jQuery – a very powerful JavaScript based UI library upon which Primefaces is based. As it is already present in a Primefaces environment, it is trivial to call and ideal for this kind of thing. In particular, it has powerful mechanisms for traversing and searching the DOM tree and selecting elements etc. If you combine this with its features for dynamically adding and removing classes from elements, it can do exactly the kind of thing that we cannot do in CSS. The jQuery website is here, and the API documentation for it may be found here.

The sample code fragments below demonstrate the idea. The sample is based on a hacked version of the the Primefaces showcase TreeTable demo. Note the following points:-

  • It is perfectly possible to add both Javascript and JSF EL inline inside a JSF component, such that the JavaScript runs at that point when the page is rendered. This example adds some JavaScript inside the JSF tags for a checkbox.
  • As the example uses JavaScript, the hightlight is added/removed both client side from a click event on the check box, and when the page is rendered based on the current check box value in the TreeTableDocument object from the server.
  • The sample uses the JSF 2.0 EL implicit object reference component.clientId to get the client ID of the component where the EL reference is used. This is new for JSF 2.0 (previously additional code would be needed for this) and like the cc implicit object used with composite components, is not at all well documented. I was unable to find reference documentation for it anywhere in the JSF 2.0 specs, but found details of it here (at the end of the post). Note that the reference needs to be placed with care – it sometimes appears to come up with ‘ghost’ client IDs which may have been allocated by JSF but do not actually appear anywhere as IDs in the HTML. When using it, check that it returns the correct ID, for example by just placing a bare #{component.clientId} on the page directly adjacent to where you are using it, and view the source of the generated page in the browser to check that the client ID it returns matches the id of the component it is used for.
  • Be careful to consider any ordering issues – the inline JavaScript call I am using must be placed on the page after the component it refers to has been rendered. By default, using component.clientId  inside a script block which is in turn inside the checkbox component tags causes the script block to be output immediately prior to the html for the checkbox. Therefore, I use two script blocks – one inside the checkbox to save the component ID in a temporary JavaScript variable, which guarantees that I obtain the correct component ID and not an incorrect or ‘ghost’ one, and then another script block immediately after the closing tag for the checkbox component which passes the form ID and saved component ID to a JavaScript function which calls jQuery to do the highlighting after the html element has been rendered on the page. Note that the client side onClick() event on the checkbox will always run after  the full page has been rendered, when the user clicks the box, so ordering is not an issue for this.
  • The call to jQuery passes in the checkbox DOM element, which jQuery wraps with a jQuery object and returns. The parent() method is then used twice to traverse the DOM tree up from the check box to the table cell <td>, and thence to the table row <tr>. The addClass() and removeClass() methods are then used to dynamically add/remove the required CSS classes to the table row depending on the passed check box state. Adding and removing classes with jQuery is useful as it does all the string bashing for you to check if the class being added/removed is already present, handles spaces correctly, and leaves all other classes on the element intact.
  • Note that in an earlier version I had tricky issues with nested quotes in JSF EL. I initially used outer double quotes, with inner single quotes on the JavaScript calls. The EL through this out however. I ended up using double quotes throughout, and escaped the inner ones with backslashes (\). This worked fine. The version shown here does not need this as it is a later modification.
  • The example extends the Primefaces Document class used in the showcase, adding a boolean property for the check box value.
  • My example uses CDI, whereas the original showcase uses JSF managed beans.

Sample Code Fragments

Index.xhtml

<!DOCTYPE HTML>
<html xmlns="http://www.w3c.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:fn="http://java.sun.com/jsp/jstl/functions"
xmlns:p="http://primefaces.prime.com.tr/ui"
xmlns:c="http://java.sun.com/jsp/jstl/core"   
xmlns:util="http://java.sun.com/jsf/composite/uk.co.salientsoft/util">
<f:view contentType="text/html">
<h:head>
    <util:themeSwitcher    outputStyle="true"
                        themeBase="/resources/themes"
                        defaultTheme="redmond"/>
<style>
    .ui-widget {
        font-size: 75% !important;
    }
    h1.header {
        font-size: 1.2em !important;
        padding: 5px;
        margin: 0px 0px 20px 0px;
    }
    h2.subheader {
        font-size: 0.9em !important;
        padding: 2px;
        margin: 0px 0px 10px 0px;
    }
    .float {
        float: left;
    }
   
    .clear {
        clear: both;
    }
    .themeswitcher {
        float: left;
        clear: both;
        margin-bottom: 10px;
    }   
    .ss-selectcolumn {
        width:30px !important;
        padding:0px !important;
        text-align:center;
    }
    .ss-checkbox {
        color:red!important;
    }
/* treetable arrow light/dark switch
    .ui-treetable tr.collapsed td .expander {
        background-image:url("/TreeTable1/javax.faces.resource/treetable/images/toggle-expand-light.png.jsf?ln=primefaces&amp;v=2.2.RC2");
    }
    .ui-treetable tr.expanded td .expander {
        background-image:url("/TreeTable1/javax.faces.resource/treetable/images/toggle-collapse-dark.png.jsf?ln=primefaces&amp;v=2.2.RC2");
    }   
*/
/* Styling nicked from default.css in the Primefaces Showcase */
    a {text-decoration: none;}
    a:hover {text-decoration: underline;}
    a img {border: none;}   
</style>

<script type="text/javascript">
//<![CDATA[
    function highlightTreeTableRow(formId, checkBoxId) {
        var checkBox = document.forms[formId][checkBoxId];
        if (checkBox.checked)
            jQuery(checkBox).parent().parent().addClass(‘ui-state-highlight ui-selected’);
        else
            jQuery(checkBox).parent().parent().removeClass(‘ui-state-highlight ui-selected’);
    }
//]]>
</script>
</h:head>
<h:body>
<h1 class="float header ui-widget ui-widget-header ui-corner-all" style="">Theme Switcher</h1>
<h:form prependId="false" id="frmTest">
<util:themeSwitcher form="frmTest" outerStyleClass="themeswitcher" style="margin-bottom:20px;"/>
<br/><br/>

<div style="width:600px;clear:both;">
    <p:treeTable value="#{documentsController.root}" var="document"
       styleClass="#{(document.name==’logo.png’)? ‘ui-state-highlight ui-selected’:’fred’}"
        expanded="true">
        <p:column>
            <f:facet name="header">
                Name
            </f:facet>
            <h:outputText value="#{document.name}" />
        </p:column>
       
        <p:column>
            <f:facet name="header">
                Size
            </f:facet>
            <h:outputText value="#{document.size}" />
        </p:column>
       
        <p:column>
            <f:facet name="header">
                Type
            </f:facet>
            <h:outputText value="#{document.type}" />
        </p:column>
       
        <p:column>
            <f:facet name="header">
                Options
            </f:facet>
            <p:commandLink update="documentPanel" oncomplete="documentDialog.show()" title="View Detail">
                <p:graphicImage value="/images/search.png"/>
                <f:setPropertyActionListener value="#{document}" target="#{documentsController.selectedDocument}" />
            </p:commandLink>
        </p:column>
       
        <p:column styleClass="ss-selectcolumn">
            <f:facet name="header">
            <h:selectBooleanCheckbox disabled="true" label="box header"/>
            </f:facet>
            <h:selectBooleanCheckbox value="#{document.selected}"
                 
onclick="highlightTreeTableRow(‘frmTest’, this.id);" styleClass="ss-checkbox" id="selector" >                
                 <script>selectId = ‘#{component.clientId}’;</script>
            </h:selectBooleanCheckbox>
            <script>highlightTreeTableRow("frmTest", selectId);</script>
        </p:column>
    </p:treeTable>
    <p:commandButton type="submit" value="Submit" onclick="submit();" style="margin-top:10px;"/>
   
    <p:dialog header="Document Detail" fixedCenter="true" effect="FADE" effectDuration="0.3"
        widgetVar="documentDialog" modal="true">
   
        <p:outputPanel id="documentPanel">
            <h:panelGrid  columns="2" cellpadding="5">
                <h:outputLabel for="name" value="Name: " />
                <h:outputText id="name" value="#{documentsController.selectedDocument.name}" style="font-weight:bold" />
               
                <h:outputLabel for="size" value="Size: " />
                <h:outputText id="size" value="#{documentsController.selectedDocument.size}" style="font-weight:bold" />
               
                <h:outputLabel for="type" value="Type " />
                <h:outputText id="type" value="#{documentsController.selectedDocument.type}" style="font-weight:bold" />
            </h:panelGrid>
        </p:outputPanel>
    </p:dialog>
    </div>
</h:form>
</h:body>
</f:view>
</html>

DocumentsController.java (modified from Showcase)

package org.primefaces.examples.view;
import java.io.Serializable;
import java.util.logging.Logger;
import javax.enterprise.context.SessionScoped;
import javax.inject.Named;
import org.primefaces.model.DefaultTreeNode;
import org.primefaces.model.TreeNode;
import uk.co.salientsoft.treetable.view.TreeTableDocument;

@Named
@SessionScoped
public class DocumentsController implements Serializable {
   
    private static final long serialVersionUID = -3593168523028092346L;
    private static final Logger logger = Logger.getLogger(DocumentsController.class.getName());
    private TreeNode root;
    private TreeTableDocument selectedDocument;
    public DocumentsController() {
        root = new DefaultTreeNode("root", null);
       
        TreeNode documents = new DefaultTreeNode(new TreeTableDocument("Documents", "-", "Folder"), root);
        TreeNode pictures = new DefaultTreeNode(new TreeTableDocument("Pictures", "-", "Folder"), root);
        TreeNode music = new DefaultTreeNode(new TreeTableDocument("Music", "-", "Folder"), root);
       
        TreeNode work = new DefaultTreeNode(new TreeTableDocument("Work", "-", "Folder"), documents);
        TreeNode primefaces = new DefaultTreeNode(new TreeTableDocument("PrimeFaces", "-", "Folder"), documents);
       
        //Documents
        TreeNode expenses = new DefaultTreeNode("document", new TreeTableDocument("Expenses.doc", "30 KB", "Word Document"), work);
        TreeNode resume = new DefaultTreeNode("document", new TreeTableDocument("Resume.doc", "10 KB", "Word Document"), work);
        TreeNode refdoc = new DefaultTreeNode("document", new TreeTableDocument("RefDoc.pages", "40 KB", "Pages Document"), primefaces);
       
        //Pictures
        TreeNode barca = new DefaultTreeNode("picture", new TreeTableDocument("barcelona.jpg", "30 KB", "JPEG Image"), pictures);
        TreeNode primelogo = new DefaultTreeNode("picture", new TreeTableDocument("logo.jpg", "45 KB", "JPEG Image"), pictures);
        TreeNode optimus = new DefaultTreeNode("picture", new TreeTableDocument("optimusprime.png", "96 KB", "PNG Image"), pictures);
       
        //Music
        TreeNode turkish = new DefaultTreeNode(new TreeTableDocument("Turkish", "-", "Folder"), music);
       
        TreeNode cemKaraca = new DefaultTreeNode(new TreeTableDocument("Cem Karaca", "-", "Folder"), turkish);
        TreeNode erkinKoray = new DefaultTreeNode(new TreeTableDocument("Erkin Koray", "-", "Folder"), turkish);
        TreeNode mogollar = new DefaultTreeNode(new TreeTableDocument("Mogollar", "-", "Folder"), turkish);
       
        TreeNode nemalacak = new DefaultTreeNode("mp3", new TreeTableDocument("Nem Alacak Felek Benim", "1500 KB", "Audio File"), cemKaraca);
        TreeNode resimdeki = new DefaultTreeNode("mp3", new TreeTableDocument("Resimdeki Gozyaslari", "2400 KB", "Audio File"), cemKaraca);
       
        TreeNode copculer = new DefaultTreeNode("mp3", new TreeTableDocument("Copculer", "2351 KB", "Audio File"), erkinKoray);
        TreeNode oylebirgecer = new DefaultTreeNode("mp3", new TreeTableDocument("Oyle bir Gecer", "1794 KB", "Audio File"), erkinKoray);
       
        TreeNode toprakana = new DefaultTreeNode("mp3", new TreeTableDocument("Toprak Ana", "1536 KB", "Audio File"), mogollar);
        TreeNode bisiyapmali = new DefaultTreeNode("mp3", new TreeTableDocument("Bisi Yapmali", "2730 KB", "Audio File"), mogollar);
    }
    public TreeNode getRoot() {return root;}
    public void setRoot(TreeNode root) {this.root = root;}
    public TreeTableDocument getSelectedDocument() {return selectedDocument;}
    public void setSelectedDocument(TreeTableDocument selectedDocument) {
        this.selectedDocument = selectedDocument;
    }
}

TreeTableDocument.java

package uk.co.salientsoft.treetable.view;
import org.primefaces.examples.domain.Document;

public class TreeTableDocument extends Document {
    private static final long serialVersionUID = 7339615098050125785L;
    public TreeTableDocument(String name, String size, String type) {
        super(name, size, type);
    }   
    private boolean;
    public boolean isSelected() {return this.selected;}
    public void setSelected(boolean selected) {this.selected = selected;}
}

No Comments »

November 26th, 2010
8:25 pm
Using container managed JPA 2.0 with CDI/EJB 3.1/Glassfish 3

Posted under JPA
Tags , , , , , ,

Update

  • One issue to be aware of, which is not immediately obvious, is when populating a database from scripts. If the version column is left null (Eclipselink leaves it as nullable by default), then the null value causes an immediate OptimisticLockException to be thrown. This is because EclipseLink expects and checks for a zero as the initial value for a new record. If EclipseLink itself persists the record, then it writes a zero.
  • One good idea to resolve this would be to enforce version columns to be non null with a zero default – although it must be said that it would be helpful if EclipsLink did this automatically in response to the @Version annotation.
  • Population scripts should therefore set version to zero, unless it is set non null with a default of zero in JPA – this latter solution is cleaner and would be automatic for me because I create version columns in a mapped superclass used by all Entities. I have yet to test this out however.

 

Original Post

This follows on from a previous post relating to JPA 1.0/Glassfish 2.1, and contains a few notes and pointers relating to JPA 2.0 under Glassfish 3 with CDI/Weld.

Firstly, when using an EJB from a client, use CDI injection (@Inject) rather than the @EJB annotation, as CDI gives many additional benefits such as type safety.

In an EJB:-

  • You are using container managed transactions, and so simply have to inject an entity manager rather than create one from an entity manager factory.
  • The container managed entity manager can be injected with a simple @PersistenceContext annotation – the persistence unit name may be defaulted and is discovered automatically.
  • The container is responsible for entity manager lifetime/closing. Calling e.g. em.close at the end of a bean method is illegal and will make Glassfish angry. You do not want to make Glassfish angry – you end up with many long stack traces in the log which on the surface do not appear to relate to this trivial problem!

A major issue with container managed transactions is how to use optimistic locking and detect and handle optimistic lock exceptions correctly. This is detailed in Pro JPA 2, pages 355-357 (you can preview this in Google Books). The main problem is that in a server environment, the OptimisticLockException will simply be logged by the container which then throws an EJBException. Trying to catch an OptimisticLockException in the calling client is therefore doomed to failure – it will probably never be caught!

The solution is to call flush() on the entity manger (e.g. em.flush()) just before you are ready to complete the method. This forces a write to the database, locking resources at the end of the method so that the effects on concurrency are minimised. This allows us to handle an optimistic failure while we are still in control, without the container swallowing the exception. The ChangeCollisionException might contain the object which caused the exception, but it is not guarranteed to. If there were multiple objects in the transaction, we could have invoked getEntity() on the caught exception to see whether the offending object was included.

If we do get an exception from the flush() call, we can throw a domain specific application exception which the caller can recognise, i.e. convert an OptimisticLockException to our own ChangeCollisionException, avoiding coupling the caller to the internal semantics of JPA transactions. It is also desirable to factor out the code which calls flush() into our own method, e.g. flushChanges() to avoid the need for every method which needs to flush having to catch the optimistic lock exception and then throw our domain specific one – we can centralise the exception conversion in our own single method.

We must annotate the ChangeCollisionException class with @ApplicationException, which is an EJB container annotation, to indicate to the container that the exception is not really a system level one but should be thrown back to the client as-is. Normally defining an application exception will cause the container not to roll back the transaction, but this is an EJB 3 container notion. The persistence provider that threw the OptimisticLockException  (in this case Eclipselink) does not know about the special semantics of designated application exceptions and, seeing a runtime exception, will go ahead and mark the transaction for rollback. The client can now receive and handle the ChangeCollisionException and do something about it.

Here is an example of our exception class with the required annotation:-

@ApplicationException
public class ChangeCollisionException extends RunTimeException {
   public ChangeCollisionException() {super(); }
}

Here are code fragment examples from a stateless session bean showing the entity manager injection, and a client JSF model bean showing the CDI injection of the stateless session bean. Note that with EJB 3.1 it is not necessary to declare local or remote interfaces for an EJB as business interfaces are not a requirement. However, I always use an interface as it allows for example a mock version of a service layer to be easily swapped in for testing. CDI supports this conveniently via alternatives, whereby a mock version of a bean can be switched in via a setting in beans.xml for testing.

@Stateless
public class SentryServiceImpl implements SentryServiceLocal {

    @PersistenceContext
    private EntityManager em;

}

@Named
@Dependent
public class ModelBeanImpl implements ModelBean, Serializable {
    private static final long serialVersionUID = -366401344661990776L;
    private @Inject SentryServiceLocal sentryService;
… 
}

With container managed JPA and/or Glassfish your persistence.xml  can also be simplified, as you can define your data source in Glassfish rather than explicitly in persistence.xml, as described here.

Note that when defining a data source directly in persistence.xml,  some property names have been standardized for JPA 2.0, so have changed from e.g. eclipselink.jdbc.user to javax.persistence.jdbcuser. The following properties in persistence.xml are now standard:-

<property name="javax.persistence.jdbc.url" value="jdbc:oracle:thin:@localhost:1521:xe"/>
<property name="javax.persistence.jdbc.user" value="sentry"/>
<property name="javax.persistence.jdbc.password" value="sentry"/>
<property name="javax.persistence.jdbc.driver" value="oracle.jdbc.OracleDriver"/>

An example persistence.xml used under JPA 2.0 follows:-

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0"
xmlns="http://java.sun.com/xml/ns/persistence"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">

    <persistence-unit name="SentryPrototype" transaction-type="JTA">
      <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
      <jta-data-source>jdbc/SentryPool</jta-data-source>
      <properties>
       <property name="eclipselink.logging.level" value="INFO" />
       <property name="eclipselink.target-database" value="Oracle" />      
       <property name="eclipselink.ddl-generation.output-mode" value="database" />      
      </properties>
     </persistence-unit>
</persistence>

No Comments »

November 15th, 2010
11:56 am
Namespace error when using JSTL in Facelets with JSF 2.0

Posted under JSF
Tags , , , , ,

Update 6/7/2011

I hit this problem again when accidentally ignoring the Eclipse warning.
This time, the problem prevented <c:if> tags from working correctly – the tags always tested as true. This was especially nasty as the cause did not appear related to the problem I was having. See this issue in Mantis about the problem.

Original Post

I was using the following namespace declaration in order to use JSTL (c:if etc.) on a Facelets/JSF 2.0 page, as I had done successfully previously under JSF 1.2:-

xmlns:c=”http://java.sun.com/jstl/core

This gave rise to the following warning in Eclipse:-

NLS missing message: CANNOT_FIND_FACELET_TAGLIB in: org.eclipse.jst.jsf.core.validation.internal.facelet.messages

The namespace is incorrect, and as pointed out here (in Ryan Lubke’s comment on the post) should now be:-

xmlns:c=”http://java.sun.com/jsp/jstl/core

This fixed the problem. A complete listing of the namespaces for the tag libraries is also available in edition 3 of Core JavaServer Faces, at the start of Chapter 4, page 101 – (edition 2 did not have this table; it is a useful addition).

No Comments »

November 11th, 2010
11:35 am
Fonts reduce in size when Primefaces table moved inside a panel

Posted under CSS
Tags , , , ,

Update 22/09/2011

The following selector (as documented below) did not work when used for a particular table. CSS ignored the rule presumably for reasons of specificity:-

.ss-nested-widget, .ss-nested-widget .ui-datatable {
     font-size: 100% !important;
}

However, the following selector did work (and was also modified to include a dialog as well as a table). This has the advantage of not relying on having ss-nested-widget as a class on the table in order to work.
This has now been adopted as standard as the solution for this issue :-

.ss-nested-widget,
.ui-widget .ui-datatable,
.ui-widget .ui-dialog { 
     font-size:100% !important;   
}

Original Post

This issue is due to font size inheritance.

My default font for all Primefaces components was specified globally as follows:-

.ui-widget {

font-size: 75% !important;

}

The reason for this is that in general it is considered more flexible to go for percentage sizes as these can then be scaled automatically when viewed on smaller devices.

Unfortunately, it appears that when for example a table is nested inside a panel, the font size appears to be 75% of 75% as the rule is inherited and applied twice, as 75% is a relative measurement, The table ends up rendered as a 9px font which is too small. The situation is further complicated by the fact that in the Primefaces skinning, there is a mixture of fonts specified in ems, percentages and pixel sizes, which means that not everything is specified in relative sizing anyway.

There are 2 solutions to the issue, both are straightforward, 1/ is the simplest although arguably not the best due to absolute sizing:-

1/ Change the global ui-widget rule above to use a fixed font size (12px works as a replacement for the above 75%)

.ui-widget {

font-size: 12px !important;

}

2/ Keep the  75%  for ui-widget and introduce a new rule to specify a 100% size for nested elements:-

.ss-nested-widget {

font-size:100% !important;

}

This rule needs to be introduced inside the panel. If it is placed on the panel itself, which is the parent element, then the font size just ‘goes large’ to 100%, i.e. larger than the desired default font. To obtain the correct behaviour, the sub-elements inside the panel must be tagged with the style class. It is possible to tag an enclosing <div> just inside the panel, so that the sub-elements inherit it. However, whilst this worked for input selectors and buttons in the panel, tables refused to play ball, and the following rule was needed to ensure that a table in the panel inherited the correct size:-

.ss-nested-widget, .ss-nested-widget .ui-datatable {

font-size: 100% !important;

}

The same kind of trick can be used to target other nested elements which do not inherit the correct size.

An alternative method would be just to add the class to sub-elements individually where required.

No Comments »

November 9th, 2010
6:54 pm
Fetching the old value in a JSF input validator

Posted under JSF
Tags , ,

In my case, I did this when checking string uniqueness in a table – if the value entered was unchanged, this would give a duplicate. Whilst I could in theory detect when I was comparing with the old value, this was hard in practice as I did not know which row I was on.

This trick is a good way of not bothering with validation if the field is unchanged. Note the need to cast the UIComponent passed in the event to a UIInput in order to expose the getValue method. Sample code is as follows :-

String oldValue = ((UIInput)component).getValue().toString().trim();

No Comments »

November 8th, 2010
1:18 pm
Header width incorrect on Primefaces scrollable table

Posted under JSF
Tags , , , , ,

If a header facet is used on a Primefaces table, the header does not allow for the scrollbars if present. the column headers do adjust for the scrollbars (although there are issues with column alignment between the headers and the data – a number of such errors have been posted to the Primefaces forum).

One workaround for this is to selectively include a CSS selector to adjust the header <div> width when the scrollbars are present.
The following selector achieves this:-

<style type=”text/css”>       

    .ss-tableheader-scrolladjust > div.ui-datatable-header {
        width:495px;
    }
</style>

The following table definition selectively includes this when enough rows are present to require scroll bars (this must be derived empirically as it depends on the height of the table, font size used etc. :-

     <h:panelGroup layout=”block” style=”width:500px;”>    
     <p:dataTable var=”car” value=”#{tableBean.carsSmall}” scrollable=”true” 
         styleClass=”#{fn:length(tableBean.carsSmall) lt 8 ? ” : ‘ss-tableheader-scrolladjust’}” height=”200″ > 
         <f:facet name=”header”>
            Ajax Sorting        
         </f:facet>
         <p:column headerText=”Model”  sortBy=”#{car.model}”>
             <h:outputText value=”#{car.model}” />
         </p:column>

         <p:column headerText=”Year”  sortBy=”#{car.year}”>
             <h:outputText value=”#{car.year}” />
         </p:column>

         <p:column headerText=”Manufacturer”  sortBy=”#{car.manufacturer}”>
             <h:outputText value=”#{car.manufacturer}” />
         </p:column>

         <p:column headerText=”Color” sortBy=”#{car.color}”>
             <h:outputText value=”#{car.color}” />
         </p:column>
     </p:dataTable>
    </h:panelGroup>

No Comments »

November 8th, 2010
12:51 pm
Setting Column Headers on a Primefaces Table

Posted under JSF
Tags , , ,

The normal way to add JSF table headers is to use a header facet :-

<p:column sortBy=”#{car.model}” >
    <f:facet name=”header”>
        <h:outputText value=”Model” />
    </f:facet>
    <h:outputText value=”#{car.model}” />
</p:column>

In Primefaces tables, this can give column width and alignment issues in some cases, so the headerText attribute of the column should be used instead :-

<p:column headerText=”Model”  sortBy=”#{car.model}”>
    <h:outputText value=”#{car.model}” />
</p:column>

No Comments »

November 8th, 2010
12:26 pm
Using JSTL Functions in JSF EL e.g. to return list size

Posted under JSF
Tags , , ,

Unfortunately some methods of backing beans, such as size() to return the number of entries in a list in a bean, are not directly callable from an EL expression as they are not exposed as standard getters. Whilst expressions on a facelets page should be used with care so as not to include business logic on the page, they are sometimes useful. One of my use cases has been to optionally add a css class into a stylclass attribute to correct the width of a table header when scrollbars are present (when enough rows are present to cause scrolling).

You can add additional properties to the beans to do this, but an easier way is often to use JSTL functions on the page. The following declaration includes the namespace for them, using prefix fn:-

<!DOCTYPE HTML>
<html xmlns=”http://www.w3c.org/1999/xhtml”
xmlns:f=”http://java.sun.com/jsf/core”
xmlns:h=”http://java.sun.com/jsf/html”
xmlns:fn=”http://java.sun.com/jsp/jstl/functions”
xmlns:p=”http://primefaces.prime.com.tr/ui”>

 

The following code fragment calls the length function to return the size of a list :-

<p:dataTable var=”car” value=”#{tableBean.carsSmall}” scrollable=”true” 
         styleClass=”#{fn:length(tableBean.carsSmall) lt 8 ? ” : ‘ss-tableheader-scrolladjust’}” height=”200″ >

 The javadoc for the complete set of jstl functions may be found here.

No Comments »

November 5th, 2010
6:34 pm
Issues with JSF h:outputStylesheet and Primefaces/jQuery skinning

Posted under JSF
Tags , , , , ,

I tried to use this whilst experimenting with dyamic style sheets, but this support post here confirms that it does not work due to an issue with the way the images are referenced.
The symptom is that you get basically correct looking layout/colours but none of the images have loaded.
The solution is to use a link tag directly e.g.:-

<link type=”text/css” rel=”stylesheet”
href=”#{request.contextPath}/resources/themes/#{themeSelectorBean.theme}/skin.css” />

Whilst on the subject of h:outputStylesheet, it should be noted that for the following example:-

<h:outputStylesheet library=”css” name=”styles.css”/>

Whilst the href of the resulting generated link tag looks like this:-

<link href=”/context-root/faces/javax.faces.resource/styles.css?ln=css”
rel=”stylesheet” type=”text/css”/>

the actual physical directory containing the style sheet in this case will be

…\WebContent\resources\css\style.css

i.e. the logical to physical mapping of the web path in this case is not straightforward and obvious. If you just create the resources folder under your project’s WebContent folder and place all your libraries as subfolders under that, it should all work correctly (the above issue with jQuery/Primefaces notwithstanding)!

No Comments »

November 4th, 2010
1:54 pm
How to add inline Javascript to a Facelets/XHTML page

Posted under JSF
Tags , , , ,

This is not immediately obvious; the following are the key points:-

  • The Javascript code between the script tags needs to be embedded in XML “CDATA” sections
  • The CDATA open and close tags need to be preceded with Javascript comment markers “//”
  • You can freely use JSF Expression language within the Javascript and it will be evaluated and inserted (as with the “request.contextPath” below)

The following example xhtml page illustrates this:-

<!DOCTYPE HTML>
<html xmlns=’http://www.w3c.org/1999/xhtml’
   xmlns:f=’http://java.sun.com/jsf/core’
   xmlns:h=’http://java.sun.com/jsf/html’>
<f:view contentType=”text/html”>
<h:head>
<script type=”text/javascript”>
//<![CDATA[
    alert(“Hello World from #{request.contextPath}”);
//]]>
</script>
</h:head>
<h:body></h:body>
</f:view>
</html>

No Comments »