Skip to content

Driving it from outside ​

Every widget is an object with methods. Nothing has to live inside it: a button elsewhere on the page, a wizard step, a keyboard shortcut can all drive it. This is flatpickr's "external elements", without the markup conventions.

Without a framework ​

js
const field = createDateTimeField(document.querySelector('#at'), {
  timeZone: 'Europe/Paris',
  locale: 'en-GB',
});

document.querySelector('#open').onclick = () => field.open();
document.querySelector('#clear').onclick = () => field.clear();
document.querySelector('#tomorrow').onclick = () =>
  field.update({ value: Temporal.Now.instant().add({ hours: 24 }) });

The buttons under this field are wired exactly like that:

nothing chosen

What each widget offers ​

all of themvalue, update(settings), clear(), destroy()
the fieldsopen(), close(), toggle(), isOpen, setIcon(node)
the calendar and the rangegoTo({ year, month }), setIcons({ prev, next })
createDateTimeRangevalue.allDay, and update({ allDay }) to set it

update() never calls onChange: it is the outside telling the widget something, not the user doing it. clear() does, because that is a choice.

update(settings) — anything, while it runs ​

Every option is a setting, and every setting can be changed after the widget exists. There is no rebuild and nothing is lost: the value the reader chose survives a change of locale, of layout, of zone.

js
field.update({ locale: 'ja-JP' });
field.update({ timeLayout: 'select' });
field.update({ timeZone: 'Asia/Tokyo' });   // the same instant, read elsewhere
nothing chosen

The last two buttons are the whole point of the library in one gesture: the value under the field never changes, and the text does. A moment read from somewhere else is a different clock face, not a different moment.

value and update({ value }) ​

Reading is a property; writing is a setting like any other. Writing does not fire onChange, so the line under this one only moves when Clear is pressed — that is a choice, and a choice is reported.

nothing chosen

goTo({ year, month }) — on a calendar ​

Moving what is shown, without touching what is chosen. A wizard step that says "pick a day in December" opens December.

js
calendar.goTo({ year: 2026, month: 12 });
nothing chosen

setIcon and setIcons ​

The field's own mark, and the calendar's two arrows, replaced while it runs.

js
field.setIcon(icon('clock'));
calendar.setIcons({ prev: icon('chevronLeft'), next: icon('chevronRight') });
nothing chosen

open, close, toggle, isOpen ​

nothing chosen

destroy() ​

Takes the widget off the page and unhooks every listener it added. A single-page application that forgets it leaks a listener per screen.

nothing chosen

In Angular ​

The same methods, reached with viewChild:

ts
@Component({
  imports: [DateTimeField],
  template: `
    <tz-datetime-field #at [(value)]="moment" timeZone="Europe/Paris" />
    <button type="button" (click)="at.open()">Open</button>
    <button type="button" (click)="at.clear()">Clear</button>
    <button type="button" (click)="jumpToToday()">Today</button>
  `,
})
export class Booking {
  readonly moment = signal<Instant | null>(null);
  private readonly field = viewChild.required(DateTimeField);

  jumpToToday() {
    this.field().open();
    this.moment.set(Temporal.Now.instant());
  }
}

A template reference — #at — is enough for buttons in the same template; viewChild is for the class. Both give the component, whose methods are the widget's.

Reacting to it ​

html
<tz-datetime-field (valueChange)="save($event)" (opened)="mark()" (closed)="blur()" />

Without a framework the same three are onChange, onOpen and onClose.

MIT