After encountering the same challenge, I finally managed to make it work just half an hour ago. My approach involved using a Stimulus controller in a Rails 6 application with the Flatpickr package integrated through Webpacker.
Since my flatpickr instances were used globally on the site, I handled instantiation in the application js file:
//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 utilized the same flatpickr instances (not ideal, I know), causing JavaScript errors in the console when trying to update the onChange configuration of the start_time
input post turbo:load. Specifically, the start_time
instance was undefined when attempting another turbo:load event to replicate the datetime value, resulting in a console error of 'Uncaught TypeError (Cannot read properties of undefined)'.
The Turbo handbook provided essential guidance that led me to the solution: "When possible, avoid attaching additional event listeners directly to elements on the page body using the turbo:load event." Hence, I developed a Stimulus controller to duplicate the flatpickr dateObj value from one 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)
})
}
}
...inserted the controller reference into the containing div of the input elements:
//_form.html.erb
<div class="card-body" data-controller="dup-datetime">
...included a data-action attribute for 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' %>
I hope this solution spares someone else several hours (or even days... weeks...) of their life.