Использование Flatpickr в связке с Vue.js требует учета особенностей реактивной модели Vue и императивной природы инициализации календаря. Flatpickr работает напрямую с DOM-элементом, тогда как Vue управляет DOM через виртуальное дерево, поэтому ключевой задачей становится корректная синхронизация жизненного цикла компонента и инстанса календаря.
Основная схема интеграции строится вокруг создания экземпляра Flatpickr после монтирования элемента и его корректного уничтожения при размонтировании компонента.
В Vue 2 и Vue 3 начальная интеграция реализуется одинаковым
принципом: доступ к DOM-элементу осуществляется после стадии
mounted.
import flatpickr from "flatpickr";
import "flatpickr/dist/flatpickr.css";
export default {
name: "DatePicker",
mounted() {
this.fp = flatpickr(this.$refs.input, {
dateFormat: "Y-m-d",
onChange: (selectedDates, dateStr) => {
this.$emit("input", dateStr);
}
});
},
beforeDestroy() {
if (this.fp) {
this.fp.destroy();
}
},
template: `
<input ref="input" type="text" />
`
};
Ключевой момент заключается в сохранении ссылки на экземпляр календаря. Это позволяет управлять его жизненным циклом и предотвращать утечки памяти.
В Vue модель данных должна быть синхронизирована с Flatpickr. Поскольку библиотека не поддерживает реактивность нативно, обновления выполняются вручную через события.
export default {
props: {
value: String
},
mounted() {
this.fp = flatpickr(this.$refs.input, {
defaultDate: this.value,
onChange: (selectedDates, dateStr) => {
this.$emit("input", dateStr);
}
});
},
watch: {
value(newVal) {
if (this.fp) {
this.fp.setDate(newVal, true);
}
}
}
};
export default {
props: {
modelValue: String
},
emits: ["update:modelValue"],
mounted() {
this.fp = flatpickr(this.$refs.input, {
defaultDate: this.modelValue,
onChange: (_, dateStr) => {
this.$emit("update:modelValue", dateStr);
}
});
},
watch: {
modelValue(newVal) {
this.fp?.setDate(newVal, true);
}
}
};
Composition API позволяет более гибко управлять жизненным циклом и ссылками на DOM.
import { ref, onMounted, onBeforeUnmount, watch } from "vue";
import flatpickr from "flatpickr";
import "flatpickr/dist/flatpickr.css";
export default {
props: {
modelValue: String
},
emits: ["update:modelValue"],
setup(props, { emit }) {
const input = ref(null);
let fp = null;
onMounted(() => {
fp = flatpickr(input.value, {
defaultDate: props.modelValue,
onChange: (_, dateStr) => {
emit("update:modelValue", dateStr);
}
});
});
watch(() => props.modelValue, (val) => {
if (fp) {
fp.setDate(val, true);
}
});
onBeforeUnmount(() => {
fp?.destroy();
fp = null;
});
return { input };
}
};
Composition API снижает связанность логики и упрощает масштабирование компонента при усложнении конфигурации календаря.
Для повторного использования логики Flatpickr удобно применять пользовательскую директиву. Такой подход подходит для форм, где календарь используется многократно.
import flatpickr from "flatpickr";
export default {
mounted(el, binding) {
el._fp = flatpickr(el, binding.value || {});
},
upd ated(el, binding) {
if (binding.value?.date) {
el._fp.setDate(binding.value.date, true);
}
},
unmounted(el) {
el._fp?.destroy();
}
};
Пример использования:
<input v-flatpickr="{ dateFormat: 'Y-m-d' }" />
Директивный подход минимизирует количество кода в компонентах, но усложняет управление состоянием при сложных сценариях.
Flatpickr не пересоздает конфигурацию автоматически при изменении props, поэтому требуется ручное обновление инстанса.
watch: {
options: {
deep: true,
handler(newOptions) {
if (this.fp) {
this.fp.se t(newOptions);
}
}
}
}
При существенных изменениях конфигурации может потребоваться пересоздание экземпляра:
this.fp.destroy();
this.fp = flatpickr(this.$refs.input, newOptions);
Такой подход особенно актуален при динамическом переключении режимов выбора дат, диапазонов или локалей.
Flatpickr поддерживает режим range, который требует отдельного контроля синхронизации.
this.fp = flatpickr(this.$refs.input, {
mode: "range",
onChange: (dates, dateStr) => {
this.$emit("update:modelValue", {
from: dates[0] || null,
to: dates[1] || null
});
}
});
При установке значения важно передавать массив дат:
this.fp.setDate([this.modelValue.from, this.modelValue.to], true);
Архитектура Flatpickr позволяет подключать плагины, которые расширяют функциональность календаря. В Vue это реализуется через передачу массива plugins при инициализации.
import rangePlugin from "flatpickr/dist/plugins/rangePlugin";
this.fp = flatpickr(this.$refs.input, {
plugins: [rangePlugin()],
mode: "range"
});
При реактивных изменениях плагинов корректнее пересоздавать экземпляр, так как динамическое подключение не всегда поддерживается без побочных эффектов.
Flatpickr поддерживает локализации через импорт языковых пакетов.
import { Russian } from "flatpickr/dist/l10n/ru.js";
this.fp = flatpickr(this.$refs.input, {
locale: Russian
});
В Vue-сценариях локаль часто зависит от состояния приложения, поэтому изменение языка требует пересоздания календаря:
watch: {
locale() {
this.fp.destroy();
this.fp = flatpickr(this.$refs.input, {
locale: this.locale
});
}
}
Причина — отсутствие явного обновления через setDate.
Vue обновляет модель, но Flatpickr остается в старом состоянии.
Возникают при отсутствии вызова destroy() в
beforeUnmount или beforeDestroy.
Flatpickr требует DOM, поэтому при серверном рендеринге требуется условная инициализация:
if (typeof window !== "undefined") {
// init flatpickr
}
В крупных приложениях Flatpickr обычно инкапсулируется в отдельный UI-компонент, который выступает адаптером между реактивной моделью Vue и императивным API календаря. Такой компонент берет на себя:
Это позволяет изолировать библиотеку от бизнес-логики и упростить тестирование компонентов формы.
Flatpickr может конфликтовать с нативным вводом текста, поэтому часто используется режим только календаря без ручного ввода:
this.fp = flatpickr(this.$refs.input, {
allowInput: false,
clickOpens: true
});
При необходимости обратной совместимости с ручным вводом требуется
дополнительная валидация строки и синхронизация через
parseDate.
При большом количестве инстансов календаря важно учитывать накладные расходы на инициализацию. Оптимизация достигается через:
Такой подход особенно критичен для таблиц, фильтров и сложных форм с повторяющимися датапикерами.