preline icon indicating copy to clipboard operation
preline copied to clipboard

hs-datepicker Date Range Not Working with Angular Reactive Forms / Detect End Date Selection

Open ShantanuMaske opened this issue 3 months ago • 1 comments

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.
  • valueChanges subscription never fires.
  • onSelect callback 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" and type: "range".
  • Show how to use onSelect to integrate with Reactive Forms and detect the end date selection.

Demo Link

NA

Expected Behavior

No response

Actual Behavior

No response

Screenshots

No response

ShantanuMaske avatar Oct 07 '25 16:10 ShantanuMaske

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 {}
  }
}

olegpix avatar Oct 13 '25 14:10 olegpix