Flatpickr onDayCreate add class

I'm struggling with a Flatpickr's (https://chmln.github.io/flatpickr/) onDayCreate event. Is here anyone who knows better how to check if picker's date object matches any date in my array of dates?

I have an array (or array of objects, not really sure how to call this) with dates going like this:

dates: {
"20161029": 3,
"20161030": 0,
"20161031": 0,
"20161101": 4,
"20161102": 4,
"20161103": 4,
"20161104": 5,
"20161105": 1,
"20161106": 0,
"20161107": 4,
"20161108": 3,
"20161109": 3,
"20161110": 4
}

And I would need to check if value is 0, >3 or 5 and add class to that date. Flatpickr has an example but it is using math function to randomize which dates should have new span element (example). But I can't configure my if else to addClass.

Answers:

Answer

I created a dictionary of the classes just for convenience. You can use the keys of your object as a way to retrieve the number associated to a day when the flatpickr is triggering the onCreateDay callback. With the value associated to a day, you can get the class from the dictionary and, if it's not empty, add it to the element.

I've added some explanations to the code in order to highlight some things I consider relevant.

You can check it running the script in this page (full screen if don't see it) or you can check it in this fiddle.

Hope it helps.

var dates = {
    20161029: 3,
    20161030: 0,
    20161031: 0,
    20161101: 4,
    20161102: 4,
    20161103: 4,
    20161104: 5,
    20161105: 1,
    20161106: 0,
    20161107: 4,
    20161108: 3,
    20161109: 3,
    20161110: 4
  },
  classDict = {
    0: 'redClass',
    1: 'greenClass',
    3: 'blueClass',
    4: 'greyClass',
    5: 'orangeClass'
  };

// Better always use a two digit format in your dates obj
function get2DigitFmt(val) {
  return ('0' + val).slice(-2);
}

// onDayCreate event, add class to day if date has a class
flatpickr("#dayCreate", {
  onDayCreate: function(dObj, dStr, fp, dayElem) {
    var date = dayElem.dateObj,
      // Note the + 1 in the month.
      key = date.getFullYear() + get2DigitFmt(date.getMonth() + 1) + get2DigitFmt(date.getDate()),
      value = classDict[dates[key]];
    if (value) {
      dayElem.className += ' ' + value;
    }
  }
});
.redClass {
  background-color: red !important;
}
.greenClass {
  background-color: green !important;
}
.blueClass {
  background-color: blue !important;
}
.greyClass {
  background-color: grey !important;
}
.orangeClass {
  background-color: orange !important;
}
<link rel="stylesheet" href="https://unpkg.com/flatpickr/dist/flatpickr.min.css">
<script src="https://unpkg.com/flatpickr"></script>
<input id="dayCreate" type="text" placeholder="Select Date..">

UPDATE

The idea of the dictionary was to simplify adding/removing classes and avoid ugly switches or long ifs. However, you could easily modify the code in order to filter by value (only values greater than 3 get class) and add any class you want when the condition is met.

For instance (fiddle):

function getClass(value) {
  // Here you could put any logic you want. Ifs, add the value to a string, whatever...
    return value === 4 ? 'redClass' : 'blueClass';
}
// onDayCreate event, add class to day if date has a class
flatpickr("#dayCreate", {
  onDayCreate: function(dObj, dStr, fp, dayElem) {
    var date = dayElem.dateObj,
      // Note the + 1 in the month.
      key = date.getFullYear() + get2DigitFmt(date.getMonth() + 1) + get2DigitFmt(date.getDate()),
      value = dates[key];
    if (value > 3) {
      dayElem.className += ' ' + getClass(value);
    }
  }
});

As you can see in the solutions I provided, there's no need to loop over the object all the time in order to get the value associated to a date, you can get it in constant time composing the key of the date from the date that flatpickr provides while constructing the day (onCreateDay callback).

UPDATE

According to the documentation (or so it seems), in order to get the date of a ccurrent day inside of the onDayCreate callback, you must use properties of fp (currentYear and currentMonth) and dayElem (textContent).

However, currentMonth returns always the month that the flatpicker is currently showing, not the month of the day (the calendar can be showing november, but the day can be in october or december) so a bit of tinkering is needed to avoid marking incorrect dates.

In this fiddle you can find a solution that doesn't use dateObj and works more like the documentation says.

And here's the code:

// Better always use a two digit format in your dates obj
function get2DigitFmt(val) {
  return ('0' + val).slice(-2);
}

function getClass(value) {
  // Here you could put any logic you want. Ifs, add the value to a string, whatever...
  return value === 4 ? 'redClass' : 'blueClass';
}

// Adjust month depending on the month's day
function getMonth(currentMonth, dayClass) {
  return currentMonth + (dayClass.contains('prevMonthDay') ? 0 : (1 + Number(dayClass.contains('nextMonthDay'))));
}

function getDateKey(year, month, day) {
  return year + get2DigitFmt(month) + get2DigitFmt(day);
}

// onDayCreate event, add class to day if date has a class
flatpickr("#dayCreate", {
  onDayCreate: function(dObj, dStr, fp, dayElem) {
    var key = getDateKey(fp.currentYear, getMonth(fp.currentMonth, dayElem.className), dayElem.textContent),
      value = dates[key];
    if (value > 3) {
      dayElem.className += ' ' + getClass(value);
    }
  }
});
Answer

Something like this should do it (intentionally verbose so you can see what's going on):

   var dates =  {
       20161029: 3,
       20161030: 0,
       20161031: 0,
       20161101: 4,
       20161102: 4,
       20161103: 4,
       20161104: 5,
       20161105: 1,
       20161106: 0,
       20161107: 4,
       20161108: 3,
       20161109: 3,
       20161110: 4
   };

    flatpickr("#dayCreate", {
      onDayCreate: function (dObj, dStr, fp, dayElem) {
        //because you're working with an object and not an array, 
        //we'll iterate using its keys
        var myDateKeys = Object.keys(dates);
        for (var i = 0; i < myDateKeys.length; i++) {
            var myDateKey = myDateKeys[i]; // "20161029 
            var myDateVal = dates[myDateKey]; // 3       
            var myYear = parseInt(myDateKey.substr(0, 4));
            var myMonth = parseInt(myDateKey.substr(4, 2));
            var myDay = parseInt(myDateKey.substr(6, 2));

            var myDateObj = new Date(myYear + '-' + myMonth + '-' + myDay + ' 00:00:00');
            var fDateObj = dayElem.dateObj;

            //compare with event date 

             if (myDateObj.getTime() == fDateObj.getTime()) {
                    $(dayElem).addClass('myFancyDateClass' + myDateVal);
             }
        }
    }
});

Then add style rules to your page to highlight the dates accordingly:

.myFancyDateClass0{
   color: red;
}
.myFancyDateClass1{
   color: green;
}
.myFancyDateClass3{
   color: blue;
}
.myFancyDateClass4{
   color: yellow;
}
.myFancyDateClass5{
   color: pink;
}
Answer

Thanks nfreeze and acontell for helping me figure this out when I think documentation for that plugin was kind of hard to understand (for me atleast).

Both answers from nfreeze and acontell worked from the start when I figured out my error with the dayElem.dateObj.

The error was due my older version of the plugin. I had 2.0.5 (which is currently the version for bower) and dateObj started working when I manually updated to version 2.0.6.

My way to actually use this was to make new object (for my own purposes for the future use) and then use every items value for class:

var obj = dates,
    keys = Object.keys(obj),
    notAvailable = {},
    fewAvailable = {},
    available = {},
    value,
    date;

    var formattedDates = keys.filter(function(key) {
        if ( !obj[key]) {
          date = moment( key, 'YYYYMMDD' ).format();
          value = 'full';
          return notAvailable[date] = value;
        } else if( obj[key] < 3 ) {
          date = moment( key, 'YYYYMMDD' ).format();
          value = 'fewAvailable';
          return fewAvailable[date] = value;
        } else if( obj[key] >= 3 ) {
          date = moment( key, 'YYYYMMDD' ).format();
          value = 'available';
          return available[date] = value;
        }
 });

 var datesForPicker = Object.assign(notAvailable, fewAvailable, available);

And then onCreate event I used that :

flatpickr("#dayCreate", {
 onDayCreate: function(dObj, dStr, fp, dayElem) {

   var date = moment(dayElem.dateObj).format(),
       value = datesForPicker[date];

   if( value ){
    dayElem.className += ' ' + value;
   }

 }
});

Use of Moment.js is logical because I already have it in my project, the reason for moment format is to get those dates in the same format. You could do that other ways too.

Tags

Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us

©2020 All rights reserved.