Thursday, August 5, 2010

GWT: Extend The DatePicker/CalendarView

We're going to start this series of articles related with GWT with a tutorial about extending the DatePicker/CalendarView.

With the DatePicker you can choose any date in a Calendar and browse through it. The DatePicker uses internally the CalendarView class to configure the way the Dates are showed.

The nice thing about the DatePicker is that is really simple to use, but this simplicity comes with a high cost for Enterprise developments: It's really hard to extend, since is highly dependent of internal classes.

One of the most requested features is the ability to limit the valid dates, basically configuring a minimum and a maximum date.

To accomplish that we need to extend the DefaultCalendarView class.

In you GWT Project create a new com.google.gwt.user.datepicker.client package in your /src directory

Go to the GWT subversion repository and copy the DefaultCalendarView file to your code.

Now we're going to start doing our changes:

Create a new constructor adding support for the minimum and maximum dates and define the new variables

private Date minDate = null;
 private Date maxDate = null;

 /**
  * Constructor.
  */
 public DefaultCalendarView() {
 }

 /**
  * Constructor.
  * 
  * @param minDate
  *            Minimum allowed date.
  * @param maxDate
  *            Maximum allowed date.
  */
 public DefaultCalendarView(Date minDate, Date maxDate) {
  this.minDate = minDate;
  this.maxDate = maxDate;
 }

Ok that was easy, now we can define which are the minimum and maximum valid dates, but that's still doing nothing by itself

The easiest way to accomplish our objective is to disable the non-valid dates in the calendar, so let's modify the ......

@Override
 public boolean isDateEnabled(Date d) {
  if (minDate != null && maxDate != null) {
   if (d.after(minDate) && d.before(maxDate)) {
    return true;
   } else {
    return false;
   }
  }
  return getCell(d).isEnabled();
 }

We are almost done. Now let's modify the update method of the CellGrid subclass.

public void update(Date current) {
    // * Another Tweak *//
    setEnabled(getDatePicker().isDateEnabled(current));
    getValue().setTime(current.getTime());
    String value = getModel().formatDayOfMonth(getValue());
    setText(value);
    dateStyle = cellStyle;
    if (isFiller()) {
     dateStyle += " " + css().dayIsFiller();
    } else {
     String extraStyle = getDatePicker().getStyleOfDate(current);
     if (extraStyle != null) {
      dateStyle += " " + extraStyle;
     }
    }
    // We want to certify that all date styles have " " before and
    // after
    // them for ease of adding to and replacing them.
    dateStyle += " ";
    updateStyle();
   }

Note that we only changed the first line of the method, so we are not enabling the cell by default anymore, but we're checking if should be enabled.

The last missing part is that the DatePicker class needs also to be extended to use our new code:

public class DatePickerEx extends DatePicker {

 public DatePickerEx(Date minimum, Date maximum) {
  super(new DefaultMonthSelector(),
    new DefaultCalendarView(minimum, maximum), new CalendarModel());
 }

 public DatePickerEx(MonthSelector monthSelector, CalendarView view,
   CalendarModel model) {
  super(monthSelector, view, model);
 }
}

For your convenience you can also download the code in this archive

We're done. Enjoy your new DatePicker.

2 comments:

  1. I think you need to normalize the input dates. If minimum or maximum are set to anything but midnight, you might have some problems. CalendarUtils has some methods that help.

    ReplyDelete
  2. Hi Eric,

    I can see problems if the Minimum date is higher than the maximum date. But I don't see which problems could appear from non-normalized (I'm not really sure that's the proper naming) Dates.

    What do you have in mind?

    ReplyDelete