Jim Driscoll's Blog

Notes on Technology and the Web

Archive for the ‘YUI’ Category

Making a YUI Calendar Component in JSF2

leave a comment »

In my last blog entry, I went over getting a YUI widget working on JSF2. This time, let’s go over what’s required to move that widget into a JSF component. No Java required, but a fair bit of JavaScript.

In a lot of ways, this is just like other components that I’ve written about. The tricks are much the same – saving values into a JavaScript context. Including scripts into the component, that sort of thing.

Let’s go over the component, one file at a time. First, the using page:

   1    <ez:yuical id="cal1" value="#{date.date1}" render="out1"/>
   2    <h:outputText id="out1" value="#{date.date1}">
   3        <f:convertDateTime pattern="MM/dd/yyyy"/>
   4    </h:outputText>

To use the component, we just pass it two attributes – value, which is a managed property of type Date, and an optional render attribute, which will be updated via an ajax call when we click on a date in the component.

Simple enough. But since we’re using the version of the YUI code that’s served from Google, we’ll have to include the code in the head.

<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/yui/2.7.0/build/yuiloader/yuiloader.js"></script&gt;

Why? Because JSF’s resource API assumes all resources are local – meaning that you can’t use the h:outputScript and h:outputStylesheet with resources external to your server. I thought about showing this example with locally available resources (it’s cleaner, of course), but thought the point was worth making. Hopefully, the 2.1 version of JSF will have the ability to specify a URL, in addition to just a local resource name.

So, that’s the using page: what’s the component look like? Not much, it turns out:

   1    <composite:interface name="yuical"
   2                         displayName="YUI Cal Component"
   3                         shortDescription="YUI Calendar Component">
   4        <composite:attribute name="value" required="true" type="java.util.Date"/>
   5        <composite:attribute name="render" required="false" type="java.lang.String"/>
   6    </composite:interface>
   8    <composite:implementation>
   9        <h:outputScript name="jsf.js" library="javax.faces" target="head" />
  10        <h:outputScript name="yuical/calendar.js" target="head" />
  12        <h:panelGrid class="yui-skin-sam" id="holdingContainer">
  13            <h:panelGroup layout="block" id="calContainer"/>
  14            <h:inputHidden id="date" value="#{cc.attrs.value}">
  15                <f:convertDateTime pattern="MM/dd/yyyy"/>
  16            </h:inputHidden>
  17        </h:panelGrid>
  18        <script type="text/javascript">
  19             demo.calendar.init("#{cc.clientId}", "#{cc.attrs.render}");
  20        </script>
  21    </composite:implementation>

The interface section (lines 1-6) just details what I’ve already gone over – two attributes, a value that’s a date, and required, and a value that’s a string, and that optionally points to an id that will be updated when the date value is selected.

Lines 9-10 are where we include the scripts for the component – and as I’ve previously mentioned, if we were to have the YUI JavaScripts locally, this is where we’d include those as well.

Lines 12-17 are the same as our previous blog, where we set up the necessary structure for the widget, as well as the hidden field which we’ll update when a date is selected in the widget.

Last, we have the script at line 19 – a simple call to init, passing in the contextual render value, so we can use more than one of these components in a page.

Now, let’s look at the JavaScript code. It’s long (for a simple demo), but much of it is the same as my previous blog, and quite a bit is simply necessary fluff. We’ll take it on in three chunks, corresponding to the three sections of the code – the setup, the init function, and the handler function.

   1    if (typeof demo == "undefined" && !demo) {
   2        var demo = {};
   3    }
   5    if (typeof demo.calendar == "undefined" && !demo.calendar) {
   6        demo.calendar = {};
   7    }
   9    if (typeof demo.calendar.contextMap === "undefined" && !demo.calendar.contextMap) {
  10        demo.calendar.contextMap = [];
  11    }

This is just a simple initialization of the objects we’ll be using the JavaScript. We escape it, so we don’t set them up twice – this lets us use more than one component in a page.

Now we’ll look at the init function that’s called within the component’s XHTML:

  13    demo.calendar.init = function init(context, render) {
  15        // record the render attribute, if applied
  16        demo.calendar.contextMap[context] = render;
  18        demo.calendar.loader = new YAHOO.util.YUILoader({
  19            base: "http://ajax.googleapis.com/ajax/libs/yui/2.7.0/build/",
  20            require: ["calendar"],
  21            loadOptional: false,
  22            combine: false,
  23            filter: "RAW",
  24            allowRollup: false,
  25            onSuccess: function() {
  26                try {
  27                    demo.calendar.cal1 = new YAHOO.widget.Calendar("demo.calendar.cal1", context+":calContainer");
  28                    demo.calendar.cal1.render();
  29                    demo.calendar.cal1.selectEvent.subscribe(demo.calendar.handleSelect, demo.calendar.cal1, true);
  30                } catch (e) {
  31                    alert(e);
  32                }
  33            },
  34            // should a failure occur, the onFailure function will be executed
  35            onFailure: function(o) {
  36                alert("error: " + YAHOO.lang.dump(o));
  37            }
  39        });
  41        //
  42        // Calculate the dependency and insert the required scripts and css resources
  43        // into the document
  44        demo.calendar.loader.insert();
  45    }

This code is almost the same as in the previous blog – naturally, since most of this is necessary for setting up the Calendar itself. Two changes, to let it be in a reusable component: line 16, which records the render attribute, and associates it with the component’s id value, and line 27, where we also take into account the component’s value.

And, finally, here’s the handler, which does most of the heavy (JSF specific) lifting for this component:

  48    demo.calendar.handleSelect = function handleSelect(type, args, obj) {
  50        if (type === "select") {
  51            var calId = obj.containerId;
  52            var index = calId.indexOf(":") + 1;
  53            var tmpindex = calId.substring(index).indexOf(":") + 1;
  54            // keep looking until you get the last child index
  55            while (tmpindex !== 0) {
  56                index += tmpindex;
  57                tmpindex = calId.substr(index).indexOf(":") + 1;
  58            }
  59            var containerId = calId.substring(0,index - 1);
  60            var dateId = containerId + ":" + "date";
  61            var dates = args[0];
  62            var date = dates[0];
  63            var year = date[0], month = date[1], day = date[2];
  65            var txtDate = document.getElementById(dateId);
  66            txtDate.value = month + "/" + day + "/" + year;
  68            var render = demo.calendar.contextMap[containerId];
  69            try {
  70                // if a render is defined for the component, then include it.
  71                if (typeof render !== "undefined" && render ) {
  72                    jsf.ajax.request(dateId,null,{
  73                        render: render,
  74                        execute: dateId
  75                    })
  76                } else {
  77                    jsf.ajax.request(dateId,null,{
  78                        execute: dateId
  79                    })
  80                }
  81            } catch (e) {
  82                alert(e);
  83            }
  84        }
  85    }

By using the passed in ID of the widget that was triggered, we obtain the id’s of the component, and from there the id of the date field (lines 51-60).

As in the previous blog, we then get the element that corresponds to the hidden field, and set it (lines 65-66).

Then retrieve the render value based on the component’s id. If there is a value (remember, it’s an optional value), use it, otherwise, don’t.

And finally, perform a jsf.ajax call with the hidden value as the executed portion – which will set it on the server.

And that’s it – we now have a component that graphically allows for selecting a date, and having it reflected on the server. Neat, huh?

Anyway, I’ll be uploading this into the Project Mojarra demo directory now that it’s complete, in the ajax-component demo. You can check out the full code there.

Feel free to ask questions, below.

(This article originally published on my java.net blog on August 9, 2009.)


Written by jamesgdriscoll

February 9, 2010 at 9:58 PM

Posted in ajax, JSF, YUI

Using the YUI Calendar widget with JSF 2

leave a comment »

If you’re not developing JSF with third party component libraries, you’re really missing out on the best part of JSF. But there’s lots of Ajax widgets out there, which contain all kinds of useful functionality. Wouldn’t it be useful to use those within your JSF pages?

The Yahoo UI library is pretty nifty stuff, and the Calendar widget is useful, pretty, and powerful. Let’s wire it into a JSF page, and bind the return of that widget to the property of a bean. How hard could it be? 71 lines, of which about 45 or so are non-boilerplate. Let’s take a look. Here’s what the page is going to look like when we’re done:
Screen Shot

And, line by line, here’s the breakout of the code – note that for this example, I’ve placed everything in one file, but you’d really want to break things out for a production environment.

   1    <?xml version="1.0" encoding="UTF-8"?>
   2    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
   3    <html xmlns="http://www.w3.org/1999/xhtml"
   4          xmlns:h="http://java.sun.com/jsf/html"
   5          xmlns:f="http://java.sun.com/jsf/core"
   6          xmlns:ui="http://java.sun.com/jsf/facelets">
   7        <h:head>
   8            <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/yui/2.7.0/build/yuiloader/yuiloader.js"></script>
   9            <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
  10            <title>Test Binding</title>
  11        </h:head>
  12        <h:body>
  13            <h:outputScript name="jsf.js" library="javax.faces" target="head" />

The above code is mostly preamble, but there’s one necessary part worth exploring – Line 8 – we must use the script tag, rather than a h:outputScript tag, since outputScript is only for local resources, and this is calling an external URL. We’re using the Yahoo loader API, which we’ll call later on line 53 to load everything we need from yahoo. We’re loading this from Google’s website, primarily to keep everything in one file – whether to use local files or Google’s copies is an interesting question, but out of scope for this blog entry.

  14            <h:form id="form1" prependId="false">
  15                <div class="yui-skin-sam">
  16                    <div id="cal1Container"></div>
  17                </div>
  18                <h:inputHidden id="date" value="#{date.date}">
  19                    <f:convertDateTime pattern="MM/dd/yyyy"/>
  20                </h:inputHidden>
  21                <p>
  22                    The set date is:
  23                    <h:outputText id="out" value="#{date.date}" >
  24                        <f:convertDateTime/>
  25                    </h:outputText>
  26                </p>

Line 15-17 are the two divs that the YUI Calendar wants to see to set itself up. Note that we’re calling calendar div “cal1Container”. This will be the only part of our page that accepts input.

Line 18-20 is an h:inputHidden field that we’ll use to store the date entered into by the Calendar widget. We call it “date”, and we’ll reference it later on in the page.

Line 21-26 is our output mechanism for this page – strictly speaking, we don’t need this at all. After all, we’re already displaying the value of the calendar widget in the widget itself. This is just a way to show that, yes, we are in fact updating the bean on the server.

  27                <script type="text/javascript">
  28                    var cal1;
  29                    var loader = new YAHOO.util.YUILoader({
  30                        base: "http://ajax.googleapis.com/ajax/libs/yui/2.7.0/build/",
  31                        require: ["calendar"],
  32                        loadOptional: false,
  33                        combine: false,
  34                        filter: "RAW",
  35                        allowRollup: false,
  36                        onSuccess: function() {
  37                            try {
  38                                cal1 = new YAHOO.widget.Calendar("cal1", "cal1Container");
  39                                cal1.render();
  40                                cal1.selectEvent.subscribe(handleSelect, cal1, true);
  41                            } catch (e) {
  42                                alert(e);
  43                            }
  44                        },
  45                        // should a failure occur, the onFailure function will be executed
  46                        onFailure: function(o) {
  47                            alert("error: " + YAHOO.lang.dump(o));
  48                        }
  49                    });
  51                    // Calculate the dependency and insert the required scripts and css resources
  52                    // into the document
  53                    loader.insert();

With the exception of lines 36-44, this code is pretty much just YUI Loader boilerplate code. In fact, much of it can even be generated automatically by utilities on the main YUI site. All that this code is doing, is simply loading all of the JavaScript and CSS files required to run the Calendar widget.

Lines 36-44 set up the calendar, display it, and then register a listener on the widget. Line 40 says that we should call the selectHandler function, whenever the cal1 component has a select event.

  55                    function handleSelect(type,args,obj) {
  56                        var dates = args[0];
  57                        var date = dates[0];
  58                        var year = date[0], month = date[1], day = date[2];
  60                        var txtDate = document.getElementById("date");
  61                        txtDate.value = month + "/" + day + "/" + year;
  62                        try {
  63                            jsf.ajax.request("date",null,{render: 'out', execute: 'date'})
  64                        } catch (e) {
  65                            alert(e);
  66                        }
  67                    }
  68                </script>
  69            </h:form>
  70        </h:body>
  71    </html>

And this selectHandler function is the last part of the page. We get the date selected in the widget, assign it to the date hidden field, and then commit it to the server via the jsf.ajax.request call. Note that we also use that ajax call to update the output field as well – though I mentioned earlier that that was not strictly required – you could just use the YUI Calendar widget with the hidden field, skipping any additional display of values.

There’s more that can be done with this example – adding two way communication between the widget and the bean, for instance, or putting this into a component. But I’ve already spent a fair bit of text on this example, that’s a topic for another day.

As always, let me know if you have any questions about this example in the comments below.

(This article originally published on my java.net blog on August 9, 2009.)

Written by jamesgdriscoll

February 9, 2010 at 9:33 PM

Posted in ajax, JSF, YUI