1

I'm trying to write a jQuery plugin for displaying and entering dates, using jQuery UI's datepicker as a starting point.

Invoking it should look like this:

jQuery(document).ready(function() {

  jQuery(document).find('input.mydatepicker').mydatepicker({
    myformat : 'mm/dd/yy'
  });

});

such that any input having class="mydatepicker" will have the mydatepicker behavior.

The catch is that I'd like myformat to support a variety of different date formats, however, whenever I use the val() function to get/set the date, I want it to be in iso8601 format yyyy-mm-dd.

So, even though I specified format mm/dd/yy above to initialize all the elements, I want:

<input type="text" class="mydatepicker" id="myelement" value="02/23/2019">

var mydate = $('#myelement').val();   //mydate = '2019-02-23'

and

$('#myelement').val('2019-02-24');

<input type="text" class="mydatepicker" id="myelement" value="02/24/2019">

Using techniques described here: Override jQuery .val() function? this is what I've tried:

(function($) {

  // options & defaults
  var defaultOptions = {
    myformat : 'mm/dd/yy'
  };

  // attach jQuery.UI datepicker with specified options
  $.fn.mydatepicker = function(options) {
    options = $.extend({}, defaultOptions, options || {});

    return this.each( function() {
      var jq = $(this);
      jq.addClass('plugin-mydatepicker').datepicker({
        dateFormat: options.myformat,
        prevText: '<i class="fa fa-angle-left"></i>',
        nextText: '<i class="fa fa-angle-right"></i>',
        constrainInput: true,
        onSelect: function(date) { 
        }
      });
    });

  }


  // override val() to get/set in ISO8601 regardless of format
  var _origVal = $.fn.val;

  $.fn.val = function(value) {
    if ( $(this).hasClass('plugin-mydatepicker') ) {
      if (arguments.length >= 1) {
        // setter
        return $(this).val('x' + value);
      } 
      else {
        // getter
        return 'x' + $(this).val();
      }
    }
    return _origVal.apply(this, arguments);
  };

})(jQuery);

A couple of things going on here:

  • I'm getting "deep recursion" error whenever I call val() and I don't really no why. I'm inexperienced in writing jQuery plugins.
  • I haven't done the myformat<->iso8601 conversion yet, open to suggestions on how to do that easily. Practically speaking, myformat could be limited to mm/dd/yy and dd/mm/yy, don't need to go crazy there.
  • To tag each element having the plugin attached, I add a class plugin-mydatepicker. Not sure if this is the best way.
  • I've seen some folks use the altFormat option in datepicker. Not interested in this approach if I can avoid it, as it requires creating a second hidden input field, and synchronization between the two.
  • I'm a bit nervous about overriding the val() function; I'd also be open to an alternative get/set function within the plugin, such as mydateVal()
  • I'm very open to using some other plugin that does this, that already exists! Just haven't found one myself...

Help?

yahermann
  • 1,539
  • 1
  • 12
  • 33

1 Answers1

0

I'm quite sure my answer is not what you expect... I think you over complicated the task here. When it comes to date manipulations, I always use moment.js. I really think that my answer is way more simple than what you dived in.

Moment can handle various date formats real easilly. Here is a simple demo where you have 3 inputs instantiated with jQuery-ui datepicker and different formats.

Now the formatting definition slightly differs between jQuery-UI datepicker and Moment...

So I used 2 data- attributes to store them both. So it is easy to set the datepicker format from the HTML markup. Then, in the fonction to retreive the ISO date, it uses the moment format to correctly parse it and convert to ISO.

$(document).ready(function() {

  // Instantiate all datepickers with their own format
  $('.mydatepicker').each(function(){
    var format = $(this).data("datepicker_format");
    console.log("Datepicker initialised with format: "+format);
    $(this).datepicker({
      dateFormat : format
    });
  });

  // A button to console log the date conversion to ISO
  $(".getDate").on("click",function(){
    var input = $(this).prev('.mydatepicker');
    
    var date = input.val();
    var format = input.data("moment_format");
    console.log("Date from the input: "+date);

    var iso_date = moment(date,format).format("YYYY-MM-DD");
    console.log("ISO date: "+iso_date);
  });

});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.css" rel="stylesheet"/>

<input type="text" class="mydatepicker" id="myelement1" value="02/23/2019" data-datepicker_format="mm/dd/yy" data-moment_format="MM/DD/YYYY">
<button class="getDate">Console log the ISO date</button>
<br>
<br>

<input type="text" class="mydatepicker" id="myelement2" value="2019-02-02" data-datepicker_format="yy-mm-dd" data-moment_format="YYYY-MM-DD">
<button class="getDate">Console log the ISO date</button>
<br>
<br>

<input type="text" class="mydatepicker" id="myelement3" value="September 19, 2019" data-datepicker_format="MM dd, yy" data-moment_format="MMM Do, YYYY">
<button class="getDate">Console log the ISO date</button>
<br>
<br>

Please run the above snippet in full page mode.

CodePen.


If you want it as a function to call from anywhere (instead of a button):
function ISO_date(datepicker){
  return moment(datepicker.val(),datepicker.data("moment_format")).format("YYYY-MM-DD");
}

Then call it like this:

var convertedDate = ISO_date( $("someSpecificDatePickerSelector") );  // Pass the datepicker element.
Louys Patrice Bessette
  • 33,375
  • 6
  • 36
  • 64
  • Thank you for the thoughtful and detailed answer. Based on your answer I will use moment.js for the conversion. However I'm dealing with a lot of legacy code that gets/sets the date as `$("someselector").val()` and `$("someselector").val('2019-02-24')` so I'm still looking for that. Plus I think this would be a useful plugin, in general, for localizing dates to each user, while consistently passing/getting iso8601 dates internally. – yahermann Feb 25 '19 at 23:19