Sunday, November 4, 2012

ADF: Smart Input Date Client Converter


Environment
  • Tested with JDeveloper / ADF 11.1.2.3
  • (Should also work for 11.1.1.x)
Use Case

To improve the User Experience in heavy data entry ADF applications for "extreme keyboard users" it would be great to have the ability to enter dates by some typical conventions, e.g.

  • Type t or T or today, tab out the input field and get the current date: 2012-11-04
  • Type +7, tab out the input field and get the current date +7 days: 2012-11-11
  • Type -7, tab out the input field and get the current date -7 days: 2012-10-28
The following use cases shows the +16 feature entered on 2012-11-04

How to do it

We are going to implement a custom client converter to get the desired result. The default functionality will be inherited from the given trinidad org.apache.myfaces.trinidadinternal.convert.DateTimeConverter since we want to keep the default conversion behavior for a given pattern. 

The server side implementation is quite easy. The only thing we override is the getClientConversion(..) and getClientLibrarySource(..) methods which are part of the org.apache.myfaces.trinidad.convert.ClientConverter interface.

The most interesting part is to wrap the default client converter implementation and put it into our own client javascript converter. The default date pattern can be set in the constructor.

The javascript Converter version uses the given default converter as a delegate and just implements to handle the special cases.

/resources/js/smartdate.converter.js

/**
 * Custom Client-Converter for smart date entry
 *
 * Copyright 2012 - enpit consulting OHG, www.enpit.de
 */


function EnpitSmartDateTimeConverter(trDateTimeConverter){
    this._trDateTimeConverter = trDateTimeConverter;
}

EnpitSmartDateTimeConverter.prototype = new TrConverter();


EnpitSmartDateTimeConverter.prototype.getAsObject = function(
  parseString,
  label){  

      try {
      if( parseString == "t" || parseString == "T" || parseString == "today"){
          return new Date();    
      }

      if( this._startsWith(parseString, "+") || this._startsWith(parseString, "-")){
          if(parseString.length == 1){
              // delegate to TrDateTimeConverter
              return this._trDateTimeConverter.getAsObject(parseString,label);
          }
          var _parsedDate = new Date();
          var _parseDays = parseInt(parseString.substring(1,parseString.length));
          var _millis =  _parseDays*86400000;
          if(this._startsWith(parseString, "-")){
            _millis = _millis*-1; 
          }
          _parsedDate.setMilliseconds( _parsedDate.getMilliseconds() + _millis );
          return _parsedDate;
      }

      } catch (e){
          // delegate to TrDateTimeConverter
          return this._trDateTimeConverter.getAsObject(parseString,label);
      }

    // delegate to TrDateTimeConverter
    return this._trDateTimeConverter.getAsObject(parseString,label);
}


EnpitSmartDateTimeConverter.prototype.getAsString = function( formatTime ){
    return this._trDateTimeConverter.getAsString(formatTime );
}

EnpitSmartDateTimeConverter.prototype.getFormatHint = function() {
    return this._trDateTimeConverter.getFormatHint();
}

EnpitSmartDateTimeConverter.prototype.setDiffInMins = function( offset )
{
    this._trDateTimeConverter.setDiffInMins(offset);
}

EnpitSmartDateTimeConverter.prototype.getDiffInMins = function()
{
  return this._trDateTimeConverter.getDiffInMins();
}

EnpitSmartDateTimeConverter.prototype.getLocaleSymbols = function()
{
  return this._trDateTimeConverter.getLocaleSymbols();
}

EnpitSmartDateTimeConverter.prototype._startsWith = function(
  value,
  prefix
  )
{
    if(value == null){
        return false;
    }
    return (value.indexOf(prefix) == 0);
}

Register the custom converter in faces-config.xml





Enter ID: enpit.faces.SmartDate
Enter Class: enpit.faces.converter.SmartDateConverter

Now we are able to use the smart DateConverter on our inputDate components, all declaratively with IDE support.

Just drag and drop the f:converter from the component palette onto the desired inputDate component 

so that it looks like

Let users enjoy the smart date input. They will love it!

Conclusion / Summary
  • Implement Custom Server Side Faces Converter
  • Implement JavaScript Client Converter
  • Register Converter in faces-config.xml
  • Use Converter from Component Palette

Further Information

Download Sample

4 comments:

  1. Very helpful. Thanks so much for sharing.

    ReplyDelete
  2. Your welcome. Thanks for the kind words!

    ReplyDelete
  3. I was recently tasked with adding smart behavior to our date/time controls-- users needed to quickly enter a series of dates and times, for example: 0429 1201, while still allowing the standard format and picker to work. And of course we wanted it to run in the browser.. I looked at several options before finding this blog entry. Augmenting the existing converter saved me a lot of time and worked like a charm! Thanks for the informative post.

    ReplyDelete
  4. Hi Matt,

    you' re welcome. Enjoy ;)

    ReplyDelete