hs-datepicker Date Range Not Working with Angular Reactive Forms / Detect End Date Selection
Summary
Selecting a start and end date does not update the Reactive Form control.
Steps to Reproduce
When using hs-datepicker with Angular Reactive Forms for selecting a date range, the form control does not update automatically, and valueChanges is not triggered. I also need a way to detect when the end date is selected in order to call a function (e.g., trigger an API call).
Current Behavior
- Selecting a start and end date does not update the Reactive Form control.
valueChangessubscription never fires.onSelectcallback works in plain JS, but documentation does not explain how to integrate this with Angular or Reactive Forms.
Expected Behavior
- Reactive Form should receive the selected date range automatically.
- Ability to detect when the end date is clicked to trigger further actions.
Additional Context / Suggestion
- Documentation should include Angular / Reactive Forms integration examples.
- Explain the difference between
type: "multiple"andtype: "range". - Show how to use
onSelectto integrate with Reactive Forms and detect the end date selection.
Demo Link
NA
Expected Behavior
No response
Actual Behavior
No response
Screenshots
No response
Hi! Preline’s datepicker is a lightweight wrapper around Vanilla Calendar Pro that provides its own event API. When integrating it with Angular Reactive Forms, the recommended approach is to let Preline initialize the datepicker, subscribe to its on('change', …) event, and update your FormControl or FormGroup manually within Angular’s zone. Make sure to properly tear down the instance on component destroy to avoid leaks. This pattern usually works well for integrating third-party UI components with Reactive Forms. E.g.:
<input
#rangeInput
id="dateRange"
type="text"
readonly
[formControl]="rangeCtrl"
data-hs-datepicker='{
"type": "range",
"mode": "custom-select",
"inputModeOptions": { "itemsSeparator": ", " }
}'
/>
import {
AfterViewInit, Component, ElementRef, NgZone,
OnDestroy, ViewChild, ChangeDetectorRef
} from '@angular/core';
import { FormControl } from '@angular/forms';
declare global {
interface Window {
HSDatepicker: any;
HSStaticMethods: any;
}
}
@Component({
selector: 'app-date-range',
templateUrl: './date-range.component.html',
})
export class DateRangeComponent implements AfterViewInit, OnDestroy {
@ViewChild('rangeInput', { static: true }) rangeInput!: ElementRef<HTMLInputElement>;
rangeCtrl = new FormControl<string | null>(null);
private dpApi?: any;
constructor(private zone: NgZone, private cdr: ChangeDetectorRef) {}
ngAfterViewInit() {
// You could initialize Preline once (globally) or here if needed:
window.HSStaticMethods.autoInit();
// If your bundle exposes it, try getting the datepicker API (true => wrapper with .on/.destroy)
const api = window.HSDatepicker.getInstance('#dateRange', true);
this.dpApi = api.element ?? api;
// Bridge the picker 'change' event into the Reactive Form inside Angular’s zone
this.dpApi.on('change', (data: { selectedDates: string | string[] }) => {
this.zone.run(() => {
const dates = Array.isArray(data.selectedDates)
? data.selectedDates
: String(data.selectedDates).split(',').map(s => s.trim());
// You could store a string or map to your own {start,end} model
this.rangeCtrl.setValue(dates.join(', '), { emitEvent: true });
// If you use OnPush change detection
this.cdr.markForCheck();
});
});
}
ngOnDestroy() {
// If your build exposes destroy, you might call it to avoid listener leaks
try { this.dpApi.destroy(); } catch {}
}
}