Использование с Vue

Использование Flatpickr в связке с Vue.js требует учета особенностей реактивной модели Vue и императивной природы инициализации календаря. Flatpickr работает напрямую с DOM-элементом, тогда как Vue управляет DOM через виртуальное дерево, поэтому ключевой задачей становится корректная синхронизация жизненного цикла компонента и инстанса календаря.

Основная схема интеграции строится вокруг создания экземпляра Flatpickr после монтирования элемента и его корректного уничтожения при размонтировании компонента.


Базовая интеграция через mounted

В 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" />
  `
};

Ключевой момент заключается в сохранении ссылки на экземпляр календаря. Это позволяет управлять его жизненным циклом и предотвращать утечки памяти.


Связывание с v-model

В Vue модель данных должна быть синхронизирована с Flatpickr. Поскольку библиотека не поддерживает реактивность нативно, обновления выполняются вручную через события.

Vue 2 вариант

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);
      }
    }
  }
};

Vue 3 вариант

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

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 снижает связанность логики и упрощает масштабирование компонента при усложнении конфигурации календаря.


Использование директивы Vue

Для повторного использования логики 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

Архитектура 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.

SSR-окружение

Flatpickr требует DOM, поэтому при серверном рендеринге требуется условная инициализация:

if (typeof window !== "undefined") {
  // init flatpickr
}

Архитектурные паттерны

В крупных приложениях Flatpickr обычно инкапсулируется в отдельный UI-компонент, который выступает адаптером между реактивной моделью Vue и императивным API календаря. Такой компонент берет на себя:

  • инициализацию инстанса
  • синхронизацию состояния
  • управление конфигурацией
  • обработку событий
  • уничтожение инстанса

Это позволяет изолировать библиотеку от бизнес-логики и упростить тестирование компонентов формы.


Управление фокусом и пользовательским вводом

Flatpickr может конфликтовать с нативным вводом текста, поэтому часто используется режим только календаря без ручного ввода:

this.fp = flatpickr(this.$refs.input, {
  allowInput: false,
  clickOpens: true
});

При необходимости обратной совместимости с ручным вводом требуется дополнительная валидация строки и синхронизация через parseDate.


Производительность при массовом использовании

При большом количестве инстансов календаря важно учитывать накладные расходы на инициализацию. Оптимизация достигается через:

  • ленивую инициализацию (только при фокусе)
  • переиспользование конфигураций
  • минимизацию пересоздания экземпляров
  • отключение лишних плагинов

Такой подход особенно критичен для таблиц, фильтров и сложных форм с повторяющимися датапикерами.