'Flatpickr onChange event to set end_date

I have two date pickers using flatpickr() - start_time and end_time. When the start_time is selected, I need the end_time to be changed to the same value that was entered as start_time.

I am trying to use the onChange hook as documented on https://flatpickr.js.org/events/#hooks.

form date pickers

<div class="start_time_result mb-6" style ="width: 30vw;">
  <%= form.label :start_time, class: 'label' %>
  <div class="flex items-center justify-between max-w-md">
    <%= form.text_field :start_time, data: { behavior: "flatpickr" }, placeholder: "Date and time select ...", class: "form-control" %>
  </div>
</div>
<div class=" field" style ="width: 30vw;">
  <%= form.label :end_time, class: 'label' %>
  <div class="end_time_result flex items-center justify-between max-w-md" >
    <%= form.text_field :end_time, data: { behavior: "flatpickr" }, placeholder: "Date and time select ...", class: "form-control required " %>
  </div>
</div>

javascript

document.addEventListener('turbolinks:load', function() {
  document.querySelector('#recurring_event a')
    .addEventListener("ajax:success", function(data, status, xhr) {
      flatpickr("[data-behavior='flatpickr']", {
        enableTime: false,
        altInput: true,
        altFormat: "F j, Y",
        minDate: "today",
        onChange: function(dateObj, dateStr) {
        end_time.set('minDate', dateStr)
      }
    })
  });
});


Solution 1:[1]

You can use the onChange event of #start_time to declare the minDate for end_time.

For example:

$(document).ready(function() {
  let start_time = $('#start_time');
  let end_time = $('#end_time');

  start_time.flatpickr({
    altInput: true,
    altFormat: "F j, Y",
    onChange: function(selectedDates) {
      end_time.flatpickr({
        altInput: true,
        altFormat: "F j, Y",
        minDate: new Date(selectedDates),
      });
    }
  });

  end_time.flatpickr({});
});
<link href="https://cdnjs.cloudflare.com/ajax/libs/flatpickr/4.6.11/flatpickr.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery.maskedinput/1.4.1/jquery.maskedinput.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/flatpickr/4.6.11/flatpickr.min.js"></script>

<input id="start_time">
<input id="end_time">

Solution 2:[2]

I struggled with the exact same challenge and (literally) just got this working within the last 30 minutes. My solution was to use a Stimulus controller in a Rails 6 app with the Flatpickr package compiled in Webpacker.

Because my flatpickr instances were used site-wide, instantiation was handled in application js:

//application.js
import { Application } from "@hotwired/stimulus";
import { definitionsFromContext } from "@hotwired/stimulus-webpack-helpers";
import flatpickr from "flatpickr";
window.Stimulus = Application.start()
Stimulus.load(definitionsFromContext(context))

document.addEventListener("turbo:load", () => {
  flatpickr("[data-datetimepicker='flatpickr']", {
    enableTime: true, 
    dateFormat: "Z",
    minDate: "today",
    altInput: true,
    altFormat: "M. j, Y h:i K", 
    allowInput: true, 
    onOpen: function(selectedDates, dateStr, instance) {
        $(instance.altInput).prop('readonly', true);
    },
    onClose: function(selectedDates, dateStr, instance) {
        $(instance.altInput).prop('readonly', false);
        $(instance.altInput).blur();
    },
  })
})

Both my start_time and end_time inputs used the same flatpickr instances (an anti-pattern, I'm sure) and I was getting JS errors in the console when I attempted to update the onChange config of the start_time input after turbo:load. Specifically, my start_time instance was undefined when I tried to use another turbo:load event to duplicate the datetime value and the console error was 'Uncaught TypeError (Cannot read properties of undefined)'.

This guidance in the Turbo handbook helped me find the solution: "When possible, avoid using the turbo:load event to add other event listeners directly to elements on the page body." So I created a Stimulus controller to duplicate the flatpickr dateObj value from one flatpickr instance to another:

//dup_datetime_controller.js
import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  static targets = ["mirror"]
  
  dup(event){
     let origin = event.target._flatpickr
     let mirror = this.mirrorTarget._flatpickr
     
     origin.config.onChange.push(function(dateObj) {
        mirror.set("minDate", dateObj)
        mirror.setDate(dateObj)
     })
  }
}

...added the controller reference to the input elements' containing div:

//_form.html.erb
<div class="card-body" data-controller="dup-datetime">

...added a data-action attribute to the start_time input:

//_form.html.erb
<%= form_with(model: @ask) do |f| %>
<%= f.text_field :start_time, required: true, data: { datetimepicker: "flatpickr", action: "input->dup-datetime#dup" }, class: 'form-control bg-white', id: "quick_starttime", readonly:'readonly' %>

...and added the Stimulus target data attribute to the end_time input:

//_form.html.erb
<%= f.text_field :end_time, required: true, data: { datetimepicker: "flatpickr", dup_datetime_target: "mirror" }, class: 'form-control bg-white', id: "quick_endtime", readonly:'readonly' %>

Hope this saves someone else a few hours (days... weeks...) of their life.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Crezzur
Solution 2 mcmaddox