Sunday, March 02, 2008

Netbeans Visual JSF: Woodstock DropDown component using Calendar and Date objects

In this example, I will create a Netbeans Visual JSF Web Application using Woodstock DropDown components which use calendar and date objects. There will be some basic AJAX and validation.

Using Netbeans 6.0.1, create a new web application and select visual jsf as a framework.



This should create a visual web application.

Open the source packages directory from the Project window and click on the SessionBean1.java file. This class represents items which should be stored in the session which we want available between page views. Add the following properties to the bottom of the file:

private GregorianCalendar startCalendar;
private GregorianCalendar endCalendar;
private Date startDate;
private Date endDate;

public GregorianCalendar getEndCalendar() {
return endCalendar;
}

public void setEndCalendar(GregorianCalendar endCalendar) {
this.endCalendar = endCalendar;
}

public Date getEndDate() {
return endDate;
}

public void setEndDate(Date endDate) {
this.endDate = endDate;
}

public GregorianCalendar getStartCalendar() {
return startCalendar;
}

public void setStartCalendar(GregorianCalendar startCalendar) {
this.startCalendar = startCalendar;
}

public Date getStartDate() {
return startDate;
}

public void setStartDate(Date startDate) {
this.startDate = startDate;
}
After adding these component, right click on the page and fix imports. These properties will persist our values when we submit our page.

Next go back to the page and add 4 DropDown List and 4 StaticText components to the page from the palette. Add a DateTimeConverter and a CalendarConverter to the page. Right click on the staticText components and bind to data. Select bind to object and select startCalendar, on the next staticText field select endCalendar. Repeat for the remaining two staticText using start and end dates. After completing this select each component, and in the properties select the appropriate converter to be applied.





At this point we need to add a little Java to the backing bean. Add the following code to the page:



public Option[] getCalendars() {
ArrayList<Option> l = new ArrayList<Option>();
ArrayList<GregorianCalendar> calendars = new ArrayList<GregorianCalendar>();
GregorianCalendar gc = (GregorianCalendar) GregorianCalendar.getInstance();
gc.setTime(new Date());
gc.setTimeZone(TimeZone.getTimeZone("EST5EDT"));
gc.set(GregorianCalendar.HOUR_OF_DAY, 0);
gc.set(GregorianCalendar.MINUTE, 0);
gc.set(GregorianCalendar.SECOND, 0);
gc.set(GregorianCalendar.MILLISECOND, 0);
for (int i = 0; i < 24; i++) {
for (int j = 0; j < 60; j += 15) {
GregorianCalendar clone = (GregorianCalendar) gc.clone();
clone.set(GregorianCalendar.HOUR_OF_DAY, i);
clone.set(GregorianCalendar.MINUTE, j);
calendars.add(clone);
}
}
gc.set(GregorianCalendar.HOUR_OF_DAY, 24);
calendars.add(gc);
Collections.sort(calendars);
for (GregorianCalendar c : calendars) {
Option option = new Option();
option.setLabel(String.format("%02d", c.get(GregorianCalendar.HOUR_OF_DAY)) + ":" + String.format("%02d", c.get(GregorianCalendar.MINUTE)));
option.setValue(c);
l.add(option);
}
return l.toArray(new Option[l.size()]);
}

public Option[] getDates() {
ArrayList<Option> l = new ArrayList<Option>();
GregorianCalendar gc = (GregorianCalendar) GregorianCalendar.getInstance();
gc.setTime(new Date());
gc.setTimeZone(TimeZone.getTimeZone("EST5EDT"));
gc.set(GregorianCalendar.HOUR_OF_DAY, 0);
gc.set(GregorianCalendar.MINUTE, 0);
gc.set(GregorianCalendar.SECOND, 0);
gc.set(GregorianCalendar.MILLISECOND, 0);
for (int i = 0; i < 24; i++) {
for (int j = 0; j < 60; j += 15) {
GregorianCalendar clone = (GregorianCalendar) gc.clone();
clone.set(GregorianCalendar.HOUR_OF_DAY, i);
clone.set(GregorianCalendar.MINUTE, j);
Option option = new Option();
option.setLabel(String.format("%02d", clone.get(GregorianCalendar.HOUR_OF_DAY)) + ":" + String.format("%02d", clone.get(GregorianCalendar.MINUTE)));
option.setValue(clone.getTime());
l.add(option);
}
}
gc.set(GregorianCalendar.HOUR_OF_DAY, 24);
l.add(new Option(gc.getTime(), String.format("%02d", gc.get(GregorianCalendar.HOUR_OF_DAY)) + ":" + String.format("%02d", gc.get(GregorianCalendar.MINUTE))));
return l.toArray(new Option[l.size()]);
}



Note: Fix the imports, make sure that you choose import com.sun.webui.jsf.model.Option as the item to be imported.

Save and clean and build the application. This will refresh the framework so we can bind the new methods to our dropDown Lists.

Go back to the Design view. Click on the first two dropdown lists and bind them to the Page1.calendar items. Click on the next two items and bind them to the dates.


Next go to the properties and set the converters on the dropdown lists as appropriate. CalendarConverter on the first two dropdown lists and DateTimeConverter to the remaining dropdowns.

Next set the selected value to the appropriate property on the SessionBean1 Data Bean. startCalendar for dropDown1 and endCalendar for the second dropdown. Repeat for the date dropdowns. Add an appropriate label for each dropdown. I named them the same as the SessionBean1 property to be bound to.

Add a button and call it submit. Your example should look like this.


Next deploy the application and confirm that it works.

Next we will add some AJAX to the dropdown items. On dropDown1 add the following to the onChange event listed under the properties:
$('form1:dropDown1').submit(), $('form1:staticText1').refresh();
Repeat on the other dropdown components, but change the tags to point to the appropriate dropDown and staticText items.

Next, click on the calendarConverter1 component and change its properties: to

dateStyle: full
timeStyle: full
timeZone:EST5EDT
type: both
Repeat the process for the dateTimeConverter1.

We will now add a validator to dropDown2 which should be the endCalendar bound component. On the properties select validate under Events. Use the default handler listed. It should be dropDown2_validate


Go to Java source and navigate to the dropDown2_validate() method. Add the following code:


public void dropDown2_validate(FacesContext context, UIComponent component, Object value) {
GregorianCalendar end = (GregorianCalendar) value;
// dropDown1 is an AJAX component so the value on the backend is updated.
GregorianCalendar start = (GregorianCalendar) dropDown1.getValue();
if (start != null) {
if (end.compareTo(start) < 0) {
throw new ValidatorException(new FacesMessage("End Time must be greater than start time."));
}
}
}
Note: You need to ensure that you have both the start and end dropDown components. If they have different names, you will need to adjust the logic above to the appropriate dropDown component ids.

Your page should look something like this when you are done.



Redeploy the application. Check to see if the staticText fields update when you change values on the dropDown lists.



The submission should fail if the endCalendar is before the startCalendar.

I hope you enjoyed this brief tutorial. The files for the application are located here: CalendarDateDropDownExample.zip

Postscript: I asked for some folks at Sun to take a look at the page. They gave me some cool and interesting criticism of the tutorial.

One item that was not clear was that there are Woodstock components for calendars and schedulers. This is really an example of using a dropDown component with java.util.Calendar and java.util.Date.

It was suggested that I should use one or the other, but not both. Since this example is done, I will not change it. I will keep this in mind for future tutorials. The criticism was that it essentially was a duplication of effort and does not clearly state the case. I agree.

Additional Notes:


The project was developed using Netbeans 6.0/6.0.1 and Visual JSF (Project Woodstock 4.1.1). The latest version of Netbeans (6.1) includes Project Woodstock 4.2.

Project Woodstock has undergone a tremendous amount of changes from 4.1.1. This includes removing Prototype (JavaScript framework) and using a custom version of Dojo toolkit. The AJAX in this application depends on Prototype. You will need to install prototype in the project by adding a jar to the libraries. Here is prototype-1.5.0.jar.

10 comments :

ROD said...

the article is good , i was need it , but i can download the file zip... thank you

From Puerto Montt Chile

John Yeary said...

I am checking with the company that is hosting the site with the examples. I will post when it is fixed.

John Yeary said...

Yeah! Its fixed. They just switched their proxy to another system and it was set to explicitly deny everything unless otherwise granted.

ROD said...

thank you sr!!!

Anonymous said...

Hi john,
the article is good, but i have a problem with the example: the staticText fields are NOT updated when the values on the dropDown lists are changing; i get a JavaScript error: "row 22; Error: object expected". This is the onchange event handler. I am using Netbeans 6.1/Glassfish and Internet Explorer 7 (in Firefox there are no error messages at all, but the static fields are also not updated.
Best regards, Eugen.

John Yeary said...

I checked against Netbeans 6.1, Woodstock 4.2, and Glassfish 9.1 UR2. I agree with Eugen that it does not work.

I originally created the application using Netbeans 6.0/6.0.1, and Woodstock 4.1.1 on Glassfish 9.1 UR1. I retested it and it does work with the previous version of Netbeans. The AJAX functionality is present.

The real problem is that Woodstock 4.2 was dramatically changed from 4.1.1. The framework does not have Prototype libraries, and a customized version of the Dojo toolkit.

I used a lot of prototype short form like $() instead of the more common form.getElementById('XXX') This resulted in the project breaking.

If you put the prototype-1.5.0.jar into the project, it will work exactly as expected.

Anonymous said...

Hi John,
you're right, the problem is the usage of $-function. I used document.getElementById(..) insted and it works also with Wodstock 4.2.
Thanks, Eugen.

John Yeary said...

I justed wanted to point out that Eugen is right about using document.getElementById() instead of my form.getElementById() which is incorrect. If you change the $() syntax, it should work as expected.

walid said...

Hi John,
thanks for this blog.
I try to change a staticText value when i validate textField id do that in my page
webuijsf:script
function changeText(){
var domNode = document.getElementById("form1:textField1");
domNode.submit();

var domNode3 = document.getElementById("form1:staticText1");
domNode3.refresh();

}

webuijsf:script

webuijsf:textField id="textField1" onChange="changeText();" style="left: 192px; top: 192px; position: absolute" validatorExpression="#{Page1.textField1_validate}"
webuijsf:button actionExpression="#{Page1.button1_action}" id="button1" style="left: 359px; top: 192px; position: absolute" text="Button"
webuijsf:staticText binding="#{Page1.staticText1}" id="staticText1" style="position: absolute; left: 456px; top: 192px" text="dfgdfgdfg"

and in the bean:

public void textField1_validate(FacesContext context, UIComponent component, Object value) {

this.getStaticText1().setText(new String("zfzdfdf"));

}

in the http monitor i have two request one for submit and othe for refresh but the value of static text not changed

thanks for your response

Walid

John Yeary said...

Hello Walid,

I tried the application on NB 6.1 Update 2, and it does not work even with the prototype library installed. I tried it on NB 6.0.1 and it worked fine. Can you check to see if this is the case with your application. If it is, then I will need to do some further investigation.

Popular Posts