Angular использует компонентную архитектуру, механизм обнаружения изменений (Change Detection), двустороннее связывание данных и систему жизненного цикла компонентов. Библиотека Pikaday создавалась как независимый JavaScript-компонент без привязки к какому-либо фреймворку, поэтому для корректной интеграции требуется учитывать особенности взаимодействия между Angular и внешним DOM-виджетом.
Основная идея заключается в том, что Angular управляет состоянием приложения, а Pikaday отвечает исключительно за визуальный выбор даты.
Наиболее распространённые сценарии интеграции:
Pikaday устанавливается через npm:
npm install pikaday
При использовании TypeScript может потребоваться установка типизации:
npm install --save-dev @types/pikaday
Импорт в компоненте:
import Pikaday from 'pikaday';
HTML-шаблон:
<input #dateInput type="text">
Компонент:
import {
AfterViewInit,
Component,
ElementRef,
ViewChild
} from '@angular/core';
import Pikaday from 'pikaday';
@Component({
selector: 'app-datepicker',
templateUrl: './datepicker.component.html'
})
export class DatepickerComponent implements AfterViewInit {
@ViewChild('dateInput')
dateInput!: ElementRef<HTMLInputElement>;
private picker!: Pikaday;
ngAfterViewInit(): void {
this.picker = new Pikaday({
field: this.dateInput.nativeElement
});
}
}
На момент выполнения конструктора компонента элемент
<input> ещё отсутствует в DOM.
Хук жизненного цикла:
ngAfterViewInit()
вызывается после полной инициализации представления, поэтому ссылка
через ViewChild уже доступна.
Каждый экземпляр Pikaday создаёт собственные обработчики событий.
При удалении компонента календарь желательно уничтожать.
import {
Component,
OnDestroy
} from '@angular/core';
export class DatepickerComponent implements OnDestroy {
private picker!: Pikaday;
ngOnDestroy(): void {
if (this.picker) {
this.picker.destroy();
}
}
}
Это предотвращает:
Pikaday предоставляет обработчик onSelect.
this.picker = new Pikaday({
field: this.dateInput.nativeElement,
onSelect: (date: Date) => {
console.log(date);
}
});
После выбора даты в консоль будет выведен объект Date.
Например:
Tue Jun 02 2026 00:00:00 GMT+0500
Обычно дата хранится в свойстве класса.
selectedDate: Date | null = null;
Инициализация:
this.picker = new Pikaday({
field: this.dateInput.nativeElement,
onSelect: (date: Date) => {
this.selectedDate = date;
}
});
Теперь выбранная дата становится частью состояния Angular-компонента.
Шаблон:
<input #dateInput>
<p>
{{ selectedDate | date:'dd.MM.yyyy' }}
</p>
Компонент:
selectedDate: Date | null = null;
После выбора даты Angular автоматически обновит отображение.
Иногда внешний JavaScript-код изменяет данные вне зоны Angular.
Для гарантированного обновления интерфейса можно использовать NgZone.
import { NgZone } from '@angular/core';
Конструктор:
constructor(
private zone: NgZone
) {}
Использование:
this.picker = new Pikaday({
field: this.dateInput.nativeElement,
onSelect: (date) => {
this.zone.run(() => {
this.selectedDate = date;
});
}
});
Так Angular точно узнает об изменении состояния.
Reactive Forms является наиболее популярным способом работы с формами в Angular.
Создание формы:
import {
FormControl,
FormGroup
} from '@angular/forms';
form = new FormGroup({
date: new FormControl('')
});
HTML:
<form [formGroup]="form">
<input
#dateInput
formControlName="date">
</form>
Создание календаря:
this.picker = new Pikaday({
field: this.dateInput.nativeElement,
onSelect: (date: Date) => {
this.form.get('date')?.setValue(date);
}
});
Теперь выбранная дата автоматически попадает в форму.
Получение значения:
const value = this.form.value;
Результат:
{
date: Tue Jun 02 2026
}
Отслеживание изменений:
this.form.get('date')
?.valueChanges
.subscribe(value => {
console.log(value);
});
Все изменения Pikaday будут поступать через поток формы.
Часто дата приходит с сервера.
this.form.patchValue({
date: new Date('2026-06-15')
});
После обновления формы следует синхронизировать календарь:
const date = this.form.get('date')?.value;
if (date) {
this.picker.setDate(date);
}
Полноценная интеграция требует передачи данных в обе стороны.
Из Pikaday в Angular:
onSelect: (date) => {
this.form.get('date')?.setValue(date);
}
Из Angular в Pikaday:
this.form.get('date')
?.valueChanges
.subscribe(date => {
if (date) {
this.picker.setDate(date);
}
});
Так состояние формы и календаря остаются согласованными.
Компонент:
selectedDate!: Date;
HTML:
<input
#dateInput
[(ngModel)]="selectedDate"
name="date">
Настройка:
this.picker = new Pikaday({
field: this.dateInput.nativeElement,
onSelect: (date) => {
this.selectedDate = date;
}
});
Такой подход работает, однако Reactive Forms обычно предоставляет больше возможностей и лучше подходит для крупных приложений.
Минимальная дата:
this.picker = new Pikaday({
field: this.dateInput.nativeElement,
minDate: new Date()
});
Максимальная дата:
this.picker = new Pikaday({
field: this.dateInput.nativeElement,
maxDate: new Date(2027, 11, 31)
});
Диапазон:
this.picker = new Pikaday({
field: this.dateInput.nativeElement,
minDate: new Date(2026, 0, 1),
maxDate: new Date(2026, 11, 31)
});
Настройка русской локали:
this.picker = new Pikaday({
field: this.dateInput.nativeElement,
i18n: {
previousMonth: 'Предыдущий',
nextMonth: 'Следующий',
months: [
'Январь',
'Февраль',
'Март',
'Апрель',
'Май',
'Июнь',
'Июль',
'Август',
'Сентябрь',
'Октябрь',
'Ноябрь',
'Декабрь'
],
weekdays: [
'Воскресенье',
'Понедельник',
'Вторник',
'Среда',
'Четверг',
'Пятница',
'Суббота'
],
weekdaysShort: [
'Вс',
'Пн',
'Вт',
'Ср',
'Чт',
'Пт',
'Сб'
]
}
});
Для отображения даты в нужном формате:
this.picker = new Pikaday({
field: this.dateInput.nativeElement,
format: 'DD.MM.YYYY'
});
Пример результата:
02.06.2026
Вместо дублирования логики удобно создать обёртку.
Шаблон:
<input #input>
Компонент:
@Component({
selector: 'app-pikaday',
templateUrl: './pikaday.component.html'
})
export class PikadayComponent {}
После этого календарь используется так:
<app-pikaday></app-pikaday>
Подобная инкапсуляция упрощает поддержку проекта.
@Input()
minDate!: Date;
@Input()
maxDate!: Date;
Создание календаря:
this.picker = new Pikaday({
field: this.input.nativeElement,
minDate: this.minDate,
maxDate: this.maxDate
});
Использование:
<app-pikaday
[minDate]="startDate"
[maxDate]="endDate">
</app-pikaday>
@Output()
dateChange = new EventEmitter<Date>();
Выбор даты:
onSelect: (date) => {
this.dateChange.emit(date);
}
Родительский компонент:
<app-pikaday
(dateChange)="onDateSelected($event)">
</app-pikaday>
Для полной поддержки Angular Forms пользовательский компонент должен реализовывать интерфейс ControlValueAccessor.
Основные методы:
writeValue(value: any): void
registerOnChange(fn: any): void
registerOnTouched(fn: any): void
setDisabledState(isDisabled: boolean): void
Такой подход позволяет использовать компонент аналогично стандартным элементам формы:
<app-pikaday
formControlName="date">
</app-pikaday>
Преимущества:
Календарь может создаваться только при необходимости.
openCalendar(): void {
if (!this.picker) {
this.picker = new Pikaday({
field: this.dateInput.nativeElement
});
}
}
Подход полезен при большом количестве полей даты на странице.
Получение даты через HTTP:
this.http
.get('/api/user')
.subscribe((data: any) => {
const date = new Date(data.birthDate);
this.picker.setDate(date);
});
После загрузки данных интерфейс автоматически отображает актуальное значение.
Наиболее распространённые причины:
ngAfterViewInit;ViewChild не найден;*ngIf;Причина часто связана с работой вне Angular Zone.
Решение:
this.zone.run(() => {
this.selectedDate = date;
});
Причина:
new Pikaday(...)
создаётся многократно без уничтожения предыдущих экземпляров.
Решение:
this.picker.destroy();
в ngOnDestroy.
Необходимо явно обновлять контрол:
this.form.get('date')
?.setValue(date);
Pikaday сам по себе не знает о существовании формы Angular.
Для крупных приложений наиболее надёжной считается схема:
Такой подход обеспечивает полную совместимость с инфраструктурой Angular, позволяет централизованно управлять настройками календаря и значительно упрощает дальнейшее сопровождение приложения.