Blog Archives

December 6th, 2010
10:45 am
Customising the Primefaces Breadcrumb control

Posted under JSF
Tags , , , , ,

This control has a preview mode which dynamically shrinks and expands the links on hover. Nice idea, but it’s rather slow to render the page on load when there are many entries, as the initial compress is done by jQuery so lots of JavaScript going on.

My preference when a lot of entries are present would be to render the control statically, and instead of dynamically expanding the entries, to have them permanently compressed but with tooltips (= html title attribute) to show the full entry on hover (as suggested here in Jenifer Tidwell’s excellent book Designing Interfaces). This would be much faster.

Unfortunately, the current control does not support that – the menu item children that make up the breadcrumb entries do have a ‘help text’ attribute which looks like it should render a title/tooltip but this does not work.

I would like at some point to make a custom version. The best way would probably be to make it a new different control, possibly in my own library but at least as an additional Primefaces control that I could easily deploy on new released without having to customise code.

Chapter 11 of Core JavaServer Faces has a good example walkthrough of a full custom component development, which helps to give a background. Customising the breadcrumb looks easy as it is just a question of enhancing existing code by adding some additional attributes and changing the encoding in the renderer to encode titles and do a static breadcrumb. The following functionality would be needed:-

ToolTipMode attribute to turn on the new mode. Other modes would possibly still be present  – no point in disabling them unless I have too. If it looks like carrying the old modes is a lot of baggage I could create a stripped down control using the old one as a framework, and only support the new modes I require. As I am keeping the existing control there is no need to duplicate its features if I don’t need to. As the rendering for the new control should be straightforward, I could even rip out all the jQuery stuff and just use some of the existing CSS techniques for rendering it, but statically. I would duplicate the classes and give my own names.

In the new mode, all entries would have a hover title.

  • ToolTipCompressThreshold attribute would be a character count for the whole breadcrumb which would trigger compression of all entries equally. This is obviously a static guess figure, but it avoids having to do any client side stuff in JavaScript and so will render fast.
  • ToolTipMaxLength attribute would be a notional maximum length in pixels for the breadcrumb (set separately from the css). When the compress threshold has been reached, this attribute would be divided by the number of breadcrumb entries to be rendered, and the resulting value used as the maximum pixel size for rendering each entry (it would be good to allow them to be shorter if they don’t use all the space, so it does not look odd). Rather than strip out characters from the entries, I would just hide the excess width via a hidden overflow in a div (as the existing control does now).

The source is all available in the source or bundled zip download. Note the following from a first look:-

  • BreadCrumb.java contains the properties for all the attributes – all driven from an enum with custom methods in it (note the technique, could use this as it enforces consistency and avoids typos etc.)
  • BreadCrumbRenderer.java renders all the html and js calls.
  • BreadCrumbTag.java – note that Primefaces 2.2M1 had this file, but 2.2RC2 does not have it. Check/compare the code – they either simplified it or it has gone missing from the latest release. I could base my version on either.
  • MenuItem.java – will probably want to look at this as each BreadCrumb item is a menu item.
  • primefaces-i.taglib.xml and primefaces-p.taglib.xml are the taglibs – I will need to define a taglib for mine (see core Javaserver faces again). Note that these are in the deployed jar but not in the sources jar.
  • faces-config.xml – defines all the component classes. As with the taglibs this is in the deployed jar but not the sources jar.

No Comments »

December 3rd, 2010
12:17 pm
Using the Primefaces Breadcrumb Component

Posted under JSF
Tags , , , ,

I did some experimentation on the best look and feel (in my humble opinion!) for this component, and found the following points along the way:-

  • By default the component was underlining all the entries. This was because all anchor tags were being underlined. The Primefaces showcase has specific rules in default.css to turn this off, so I added them too. (You might have expected this to all be done by the themes or breadcrumb css, but not the case). I just added “a {text-decoration: none;}” and “a:hover {text-decoration: underline;}” rules, but obviously there are other ways if you want other links to be underlined.
  • I then tried to place a breadcrumb in a scrolling div, to ensure that it would still work if the content overflowed. I knew that horizontal scroll bars on a breadcrumb would not look at all sexy, but wanted at least some solution for edge cases with extreme amounts of content, as by default the component truncates to the right and content is lost/not accessible. I was unable to solve this issue – the component would not play ball. I wanted to leave an inner div containing the component at a floating size, so that the control sized itself for the content, and then add a scrollable outer div with overflow:auto so that it would scroll if needed. However, the component appears to use some jQuery magic to detect the size of any fixed ancestor outer level div of the component, and then fix the control width to that size using inline styling added to one of its own inner divs by jQuery/Javascript. The only partial solution was to have a large fixed size component in a smaller outer scrolling div, but this was not good as the scrollbars were present even if not needed as the control had a fixed size.
  • The problem could probably be solved using jQuery, perhaps by modifying the jBreadcrumb jQuery component which Primefaces uses, but I did not investigate this further.
  • In the end, I reconciled myself with the default way the component works. It does have a good solution to this issue by (optionally) dynamically collapsing the entries to a designated preview width, and then expanding an entry on mouse hover (so-called preview mode). This does save a lot of space, and I decided that I could easily have more than enough entries for my needs in say an 800 pixel wide breadcrumb. Alternatively the component can be left to float entirely and rely on the browser for scrolling, but in practice there will generally be an outer container of some kind which will set a limit and trigger the above issue. In my case, this would likely be an outer level tab for a form.
  • I also decided that in practice it would be helpful to turn the preview mode on dynamically, perhaps say when the number of entries exceeded a certain number, or better still when the total character count in the breadcrumb exceeds a certain figure, as this would give good results on average and still be easy to calculate. For normal use, when there is plenty of room left in the breadcrumb, preview mode is a nuisance, but it is obviously essential when the component fills up. Ideally, this would be done when the component is filled rather than based on an entry count, but this would be tricky to calculate. Ideally this would be done dynamically by the component itself. Best of all might be a dynamic preview mode which shrinks the entries only when required and only enough to fit everything in the component.
  • Rendering the component in preview mode takes significant time when there are a lot of entries (even on my Core I7 920). This might be an issue when partially updating a page (and the component) during navigation. This is even more reason not to use preview mode unless it is really essential, as with it turned off the breadcrumb renders very quickly. On some occasions it would be possible to update the control client side, for example when just changing the underline/italic decoration on the text (see below re traversing a hierarchy) and no entries are added or removed. In this case the necessary classes could just be added and removed from the breadcrumb entries dynamically with jQuery. I have also not tried any Ajax/partial form updates with it, for example just adding or removing a single entry, but I expect the whole component will be re-rendered each time as the manual makes no mention of partial updating.
  • I experimented a bit with the expand and contract timings in preview mode. These interact with the time taken by the JavaScript to dynamically collapse the entries when the component is first rendered, so if you try to make them too fast it looks clunky. Also if the expand is too fast it can confuse you as to which entry you are hovering on, especially when there is a lot of preview compression, as you can end up highlighting the next entry along without realising it, just due to the expand/collapse effect.
  • One common use case for me is to use the component to hold the navigation path when traversing a hierarchical data set. A desirable feature would be that when you traverse back up the tree say by clicking on one of the higher levels in the breadcrumb, you do not immediately lose all the lower levels you visited. They would only be removed if you visited a higher level and then traversed down a different lower level path at some point, in which case the lower levels visited previously beyond that point would then be removed and a new path built.
  • The nice thing about the above concept is that you can repeatedly wander freely up and down a given tree path via the breadcrumb without losing any of the context, which is handy in my case as the UI deliberately only shows the current level of the tree in a table – no tree or treetable components are used in order to keep things intuitive and simple for novice users.
  • When applying this concept, it is necessary to highlight the current location in the breadcrumb, as this will no longer always be the rightmost entry. The most logical way to do this seemed to apply the same highlight as the mouse hover, which is to underline an entry. Therefore, the current entry will always be underlined, as will any other entry that is hovered over.
  • In addition, it is desirable to distinguish between the breadcrumb levels between the root and the current point from those previously visited below the current point, as the breadcrumb is no longer only showing the path to where you currently are. I tried various kinds of dimmed, disable style highlighting, but whilst it is important to make these previously visited lower level entries look different, it must also be clear that you can still click on them. In the end, my preferred solution which looks good in all the top 5 browsers and with all the Primefaces themes was to italicise the previously visited lower level entries. You therefore end up with ‘normal’ styling up to the current level. The current level itself is then shown underlined so it is clear where you are. All the entries to the right (if any) are then italicised.

The following example shows my preferred component, which is somewhat thinner than the default due to the removed padding. Examples of the different highlighting discussed above are all just coded directly into the menu options. The example also uses my theme switcher component which is not listed.

 

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;
        float: left;
    }
    .themeswitcher {
        float: left;
        clear: both;
        margin-bottom: 20px;
    }   
    .ss-breadcrumb {
        clear:both;
        margin: 0 0 10px 0;
        padding: 0;
        width:800;
    }   
    a {text-decoration: none;}
    a:hover {text-decoration: underline;}
    .ss-currentlevel {text-decoration:underline;}
    .ss-lowerlevel {font-style: italic;}
</style>

</h:head>
<h:body>
<h1 class="header ui-widget ui-widget-header ui-corner-all" style="">Breadcrumb</h1>

<h:form id="frmTest">
<util:themeSwitcher form="frmTest" outerStyleClass="themeswitcher"/>
<br/><br/>
    <p:breadCrumb styleClass="ss-breadcrumb" preview="false">
        <p:menuitem value="Categories" url="#" />
        <p:menuitem value="Sports" url="#" />
        <p:menuitem value="Football" url="#" />
        <p:menuitem value="Countries" url="#" styleClass="ss-currentlevel"/>
        <p:menuitem value="Spain" url="#"  styleClass="ss-lowerlevel"/>
        <p:menuitem value="F.C. Barcelona" url="#" styleClass="ss-lowerlevel"/>
        <p:menuitem value="Squad" url="#" styleClass="ss-lowerlevel"/>
        <p:menuitem value="Lionel Messi" url="#" styleClass="ss-lowerlevel"/>
        <p:menuitem value="Squad" url="#" styleClass="ss-lowerlevel"/>
        <p:menuitem value="Lionel Messi" url="#" styleClass="ss-lowerlevel" />
    </p:breadCrumb>
    <p:breadCrumb styleClass="ss-breadcrumb" preview="true" previewWidth="24"
        expandEffectDuration="400" initialCollapseEffectDuration="100"
        expandedBeginningItems="1" expandedEndItems="1">
        <p:menuitem value="Categories" url="#" />
        <p:menuitem value="Sports" url="#" />
        <p:menuitem value="Football" url="#" />
        <p:menuitem value="Countries" url="#" />
        <p:menuitem value="Spain" url="#" />
        <p:menuitem value="F.C. Barcelona" url="#"  styleClass="ss-currentlevel"/>
        <p:menuitem value="Squad" url="#" styleClass="ss-lowerlevel" />
        <p:menuitem value="Lionel Messi" url="#" styleClass="ss-lowerlevel" />
        <p:menuitem value="Categories" url="#" styleClass="ss-lowerlevel" />
        <p:menuitem value="Sports" url="#" styleClass="ss-lowerlevel" />
        <p:menuitem value="Football" url="#" styleClass="ss-lowerlevel" />
        <p:menuitem value="Countries" url="#" styleClass="ss-lowerlevel" />
        <p:menuitem value="Spain" url="#" styleClass="ss-lowerlevel" />
        <p:menuitem value="F.C. Barcelona" url="#" styleClass="ss-lowerlevel" />
        <p:menuitem value="Squad" url="#" styleClass="ss-lowerlevel" />
        <p:menuitem value="Lionel Messi" url="#"  styleClass="ss-lowerlevel" />
    </p:breadCrumb>
</h:form>
</h:body>
</f:view>
</html>

No Comments »

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 16th, 2010
2:59 pm
JSF 2.0 Composite Components

Posted under JSF
Tags , , , , , ,

This post highlights a few lessons learned whilst experimenting with a simple composite component, in this case an experimental  Primefaces theme switcher component. The component does the following:-

  • Automatically iterates the themes directory on the server to build a list of available themes which are presented for the user to select
  • Generates a simple theme control panel with a border, consisting of a drop down combo listing the themes, with previous theme/next theme buttons to the right.
  • Also allows theme switching via ctrl/shift/left arrow and ctrl/shift/right arrow, to give a quick way of cycling through the themes when choosing one.
  • The component is used on the JSF page in two modes – the first usage is with outputStyle=”true”, themeBase and defaultTheme attributes. It is placed in the <head> section, and it defines the base path for all the theme directories, and the default starting theme (which may be programmatically overriden by setting the theme property in its backing bean, themeSwitcherBean). This mode also outputs the link directive to link the current theme stylesheet to the page. It does not result in the display of the control panel.
  • The second mode is used inside a <form> on the page, and results in the theme switcher panel being displayed.
  • A theme switch results in a form submission, so this must be acceptable to the form logic. If not, i.e. if the form is dirty, the theme switcher must be disabled. This would be done by passing a disable flag attribute to the component, which maps to a disable property of the backing bean, which results in enabling/disabling the controls. At present, a disable feature is not supported.

 

The following salient points were picked up along the way:-

  • The component does not have to be placed in a form. The examples in the Core JavaServer Faces book seemed to always show the client page with the control placed on a form, and also with <form></form> tags present in the custom component implementation. There is absolutely no requirement for the custom component implementation definition to be in its own form.
  • With JSF 2.0, it is easy to pass a value e.g. from a composite component attribute in a call, direct to a backing bean property, as JSF 2.0 now supports method expressions with parameters in EL expressions – just add the brackets and the call arguments. This used to be tricky, and many posts on technical blogs just seemed to answer ‘why on earth do you want to’. Well, perhaps it is unusual, but in this example it feels just the right thing. Normally when linking to a style sheet, one would put the path to the sheet directly on the page in the <link> tag, as one also would when referring to other resources such as images. I have therefore done exactly the same with the themeswitcher – you pass the theme base web context path (along with the default theme name) to the component just as you would do to a <link> tag. This then allows the backing bean to discover the physical theme directory location on disk and automatically iterate it to generate a list of the available themes which are present, which is then passed to the select list of the combo. Whilst the theme base context path could be set up as an injected property of the backing bean, it just does not feel right to do it that way, as normally this knowledge is encapsulated on the page. Sometimes, passing values from the page to a bean is actually the right thing to do!
  • When using an EL method expression to write to a bean property, note that you can just call a setter on the bean directly as if it was a method, giving the “set” prefix, and passing the value as an argument. You literally just plant the EL call on its own in the middle of the page. (You can of course also use another custom method if you wish, for example if you want to pass a number of properties in a single call). Furthermore, when calling for example a setter or any other method that returns void, nothing is written to the page, as you would expect. In the example code below, the EL expression #{cc.attrs.bean.setThemeBase(cc.attrs.themeBase)} does exactly this.
  • A composite component acts as a JSF naming container. Naming containers are required to ensure uniqueness of IDs on a page , by modifying Ids to add a prefix. For example a composite component could be placed many times on a page, and the Ids of all its constituent components/controls must be unique. As a test example, when the above component was placed in the <head> section (i.e. not in a form), its clientId was j_idt4. When placed on the page a second time, in the form to show the control panel , its clientId was frmTest:j_idt9. Notice that by default the clientId was automatically prefixed by the form ID. This issue becomes important when Javascript is in use to refer to controls in the component, as the Id given to a control is in turn prefixed by the clientId. For example, when on the form, a select box which was given an Id of cmbTheme actually ended up with an id of frmTest:j_idt9:cmbTheme. This needs to be taken account of in the code. Note that in the Javascript examples in Chapter 9 of Core JavaServer Faces (p369), the form is inside the composite component. This turns things around, as the form ID is then prepended by the clientId of the composite component as the latter is a naming container – our case is different.
  • By default a form’s controls have the form id prefixed to their id, as the form is also a naming container. This behaviour can be disabled if desired by adding the attribute prependId=”false” to the form tag, but I did not do this.
  • JSTL functionality can be used such as c:if, and for example you can test parameters passed to composite components (cc.attrs.attrname)– the themeswitcher example uses this with the outputStyle attribute to determine whether to output a <link> tag for the theme stylesheet and set the theme base directory/default theme, or whether to output the themeswitcher control panel. The use of c:if in this way is a powerful means of controlling exactly what a custom component renders on the page, and is a useful alternative to using the render property on a component to prevent it being rendered. In addition, it can for example conditionalise the setting of backing bean properties from the page, as is done in the themeswitcher, which could not be done via toggling the render property on components.  In this case, it seemed more convenient and clearer to give two modes to the same component, so that all the design decisions where encapsulated and visible in one place, rather than inventing another component. Other examples of where this might be useful is where a general purpose composite component might be tailorable via its call attributes. For example a table component might have adjacent up/down buttons to allow re-ordering/moving of a block of selected rows, but the re-order capability might not always be needed, so it could be conditionally selected with c:if.
  • When using JSTL, there are limitations. As an example, I tried c:set to copy an attribute containing the backing bean reference to another variable, to use later in a subsequent instance of the composite component and save passing it twice. Setting a valueChangeListener using the saved copy of the bean reference gave a run time error and is not supported. However, reading and writing properties on the bean using the saved copy of the reference worked fine.
  • Note that other JSTL functions can also be used, e.g. to return the size of a string or list, as noted here.

Comments Off on JSF 2.0 Composite Components

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 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
3:11 pm
Changing Primefaces Structural CSS – Thin Panel example

Posted under JSF
Tags , , ,

Update 10/5/2011

The example project which contains this example is PrimeThemeSwitcherTableNonCC, on page ThinPanel.xhtml. This project may be found in the repository here.

Note that the styling for the repository version has changed slightly from below and is as follows:-

div.ss-thinpanel.ui-panel{
    padding:0px !important;
    border-top-width: 1px !important;
    border-right-width: 1px !important;
    border-bottom-width: 1px !important;
    border-left-width: 1px !important;
    margin-bottom: 1px !important;
}
.ss-thinpanel > div.ui-panel-titlebar {
    padding-top: 4px !important;
    padding-bottom: 3px !important;
    border:none !important;
}

 

Original Post

Sometimes it is desirable to change not the skin of a component but its structure – size, borders etc.
One example of mine was when I wanted an accordion like component which allows multiple panels to be open simultaneously. The accordion used to allow this but does not any longer. A good alternative to the accordion in this case is a stack of panel components, as they can all have independent expand/collapse buttons, and the close button is optional and so can be removed. As a bonus, a submenu can be added if desired as shown in the component showcase.

My issue with the panel for this use case was simply that it looked too chunky and took up too much real estate on the screen when a stack of them was present. I therefore produced some alternative structural CSS to produce a weight watcher’s version of panel, with the aim that a stack of them would take up no more vertical space than a similar stack of the same number of accordion tabs. In fact, in the end my weight watcher’s panel has ended up slightly slimmer than the accordion.

The following example page shows a stack of 3 standard ‘full fat’ panels followed by a similar stack of 3 of the thin version.
Note the following important points about the way the CSS is implemented:-

  • The CSS selectors are all driven by the CSS class ss-thinpanel. Simply including this in the styleClass attribute of the p:panel component enables the thin version.
  • As the new version is tied to the new class ss-thinpanel I have done nothing in CSS which would affect existing use of the panel component – as the example shows, the old can be used alongside the new.
  • The CSS overrides the structural CSS for the panel component, not the skinning CSS. This way, the changes work consistently across all the themes and do not require any theme changes to be made.
  • This technique is useful for any occasion where an alternative flavour of a component is needed – just ensure that the structural CSS is targeted (so all themes work), and drive the change by a new CSS class name. You can then have a number of alternative flavours of a component which are useable just by including the required classname.

<!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:p="http://primefaces.prime.com.tr/ui">
<f:view contentType="text/html">
<h:head>
    <link type="text/css" rel="stylesheet"
     href="#{request.contextPath}/resources/themes/eggplant/skin.css" />

<style type="text/css">       
        .ui-widget, .ui-widget .ui-widget {
            font-size: 75% !important;
        }
        div.ss-thinpanel.ui-panel {
            padding:0px !important;
            border-top-width: 1px !important;
            border-right-width: 1px !important;
            border-bottom-width: 1px !important;
            border-left-width: 1px !important;
            margin-bottom: 1px !important;
        }
        .ss-thinpanel > div.ui-panel-titlebar {
            padding-top: 3px !important;
            padding-bottom: 2px !important;
            border:none !important;
        }
    </style>
</h:head>
<h:body>
<h:form id="frmTest">
    <h:panelGroup layout="block" style="width:800px;">
        <p:panel toggleable="true" toggleSpeed="500" collapsed="true" header="F.C. Barcelona">
            <h:outputText value="FC Barcelona is one of only three clubs never to have been relegated from La Liga and is the most successful club in Spanish football along with Real Madrid,
            having won twenty La Liga titles, a record twenty-five Spanish Cups, eight Spanish Super Cups, four Eva Duarte Cups and two League Cups.
            They are also one of the most successful clubs  in European football having won fourteen official major trophies in total,
            including ten UEFA competitions. They have won three UEFA Champions League titles, a record four UEFA Cup Winners’ Cups,
            a record three Inter-Cities Fairs Cups (the forerunner to the UEFA Europa League), three UEFA Super Cups and one FIFA Club World Cup.
            The club is also the only European side to have played continental football in every season since its inception in 1955." />
        </p:panel>
        <p:panel toggleable="true" toggleSpeed="500" collapsed="true" header="F.C. Barcelona">
            <h:outputText value="FC Barcelona is one of only three clubs never to have been relegated from La Liga and is the most successful club in Spanish football along with Real Madrid,
            having won twenty La Liga titles, a record twenty-five Spanish Cups, eight Spanish Super Cups, four Eva Duarte Cups and two League Cups.
            They are also one of the most successful clubs  in European football having won fourteen official major trophies in total,
            including ten UEFA competitions. They have won three UEFA Champions League titles, a record four UEFA Cup Winners’ Cups,
            a record three Inter-Cities Fairs Cups (the forerunner to the UEFA Europa League), three UEFA Super Cups and one FIFA Club World Cup.
            The club is also the only European side to have played continental football in every season since its inception in 1955." />
        </p:panel>
        <p:panel toggleable="true" toggleSpeed="500" collapsed="true" header="F.C. Barcelona">
            <h:outputText value="FC Barcelona is one of only three clubs never to have been relegated from La Liga and is the most successful club in Spanish football along with Real Madrid,
            having won twenty La Liga titles, a record twenty-five Spanish Cups, eight Spanish Super Cups, four Eva Duarte Cups and two League Cups.
            They are also one of the most successful clubs  in European football having won fourteen official major trophies in total,
            including ten UEFA competitions. They have won three UEFA Champions League titles, a record four UEFA Cup Winners’ Cups,
            a record three Inter-Cities Fairs Cups (the forerunner to the UEFA Europa League), three UEFA Super Cups and one FIFA Club World Cup.
            The club is also the only European side to have played continental football in every season since its inception in 1955." />
        </p:panel>
    </h:panelGroup>
    <br/>
    <h:panelGroup layout="block" style="width:800px;">
        <p:panel toggleable="true" styleClass="ss-thinpanel" toggleSpeed="500" collapsed="true" header="F.C. Barcelona">
            <h:outputText value="FC Barcelona is one of only three clubs never to have been relegated from La Liga and is the most successful club in Spanish football along with Real Madrid,
            having won twenty La Liga titles, a record twenty-five Spanish Cups, eight Spanish Super Cups, four Eva Duarte Cups and two League Cups.
            They are also one of the most successful clubs  in European football having won fourteen official major trophies in total,
            including ten UEFA competitions. They have won three UEFA Champions League titles, a record four UEFA Cup Winners’ Cups,
            a record three Inter-Cities Fairs Cups (the forerunner to the UEFA Europa League), three UEFA Super Cups and one FIFA Club World Cup.
            The club is also the only European side to have played continental football in every season since its inception in 1955." />
        </p:panel>
        <p:panel toggleable="true" styleClass="ss-thinpanel" toggleSpeed="500" collapsed="true" header="F.C. Barcelona">
            <h:outputText value="FC Barcelona is one of only three clubs never to have been relegated from La Liga and is the most successful club in Spanish football along with Real Madrid,
            having won twenty La Liga titles, a record twenty-five Spanish Cups, eight Spanish Super Cups, four Eva Duarte Cups and two League Cups.
            They are also one of the most successful clubs  in European football having won fourteen official major trophies in total,
            including ten UEFA competitions. They have won three UEFA Champions League titles, a record four UEFA Cup Winners’ Cups,
            a record three Inter-Cities Fairs Cups (the forerunner to the UEFA Europa League), three UEFA Super Cups and one FIFA Club World Cup.
            The club is also the only European side to have played continental football in every season since its inception in 1955." />
        </p:panel>
        <p:panel toggleable="true" styleClass="ss-thinpanel" toggleSpeed="500" collapsed="true" header="F.C. Barcelona">
            <h:outputText value="FC Barcelona is one of only three clubs never to have been relegated from La Liga and is the most successful club in Spanish football along with Real Madrid,
            having won twenty La Liga titles, a record twenty-five Spanish Cups, eight Spanish Super Cups, four Eva Duarte Cups and two League Cups.
            They are also one of the most successful clubs  in European football having won fourteen official major trophies in total,
            including ten UEFA competitions. They have won three UEFA Champions League titles, a record four UEFA Cup Winners’ Cups,
            a record three Inter-Cities Fairs Cups (the forerunner to the UEFA Europa League), three UEFA Super Cups and one FIFA Club World Cup.
            The club is also the only European side to have played continental football in every season since its inception in 1955." />
        </p:panel>
    </h:panelGroup>
</h:form> 
</h:body>
</f:view>
</html>

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 »