'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 |