Angular
Des composants standalone posés sur @tzslot/dom, pour Angular 18 à 22. Signaux, formulaires, zoneless ou non, aucun design system et aucun CDK.
Mise en place
// angular.json → architect.build.options
"styles": ["@tzslot/theme/tzslot.css", "src/styles.css"]Angular exige un chemin qui finit par .css ; "@tzslot/theme" seul est refusé.
import { Component, signal } from '@angular/core';
import { Calendar, TimeSlotPicker } from '@tzslot/angular';
import type { Instant, PlainDate } from '@tzslot/core';
@Component({
selector: 'app-booking',
imports: [Calendar, TimeSlotPicker],
template: `
<tz-calendar [(value)]="day" [buttons]="['today', 'clear']" />
@if (day(); as chosen) {
<tz-time-slots [date]="chosen" timeZone="Europe/Paris" [(value)]="moment" />
}
`,
})
export class Booking {
readonly day = signal<PlainDate | null>(null);
readonly moment = signal<Instant | null>(null);
}Avec des NgModules, mettez les mêmes composants dans les imports du module : ils sont standalone.
Les composants
| Choisit | |
|---|---|
<tz-calendar> | un jour |
<tz-multi-date> | plusieurs jours, pas forcément consécutifs |
<tz-date-field> | un jour, depuis un champ qui ouvre un panneau |
<tz-datetime-field> | un moment : un calendrier et une heure dans un panneau |
<tz-date-range> | deux jours |
<tz-time-slots> | un moment parmi les créneaux du jour |
<tz-datetime-range> | un intervalle : deux champs date-et-heure |
<tz-daily-range> | une plage de jours avec les mêmes horaires chaque jour |
<tz-range-field> | une période, en un champ — sa propre page |
<tz-time-select> | une heure, dans deux menus qui connaissent le jour |
<tz-time-input> | une heure, dans un champ compact à flèches |
Chacun, en train de tourner. Les wrappers ajoutent les signaux, les formulaires et les idiomes d’Angular par-dessus exactement ceux-là :
Partir d’une valeur
Un écran qui modifie quelque chose a sa valeur avant que le composant n’existe. Liez-la : elle est à l’écran au premier rendu, sans clic ni tick.
readonly day = signal(Temporal.PlainDate.from('2026-09-23'));
readonly at = signal(Temporal.Instant.from('2026-09-23T12:30:00Z'));<tz-calendar [(value)]="day" />
<tz-datetime-field [(value)]="at" timeZone="Europe/Paris" />Idem pour un FormControl construit avec une valeur — y compris un Date, sous valueAs="date". Le composant l’affiche et laisse le contrôle pristine : afficher une valeur n’est pas l’utilisateur qui en saisit une.
form = new FormGroup({
day: new FormControl<Date | null>(new Date('2026-09-23T10:00:00Z')),
});Écrire dans le signal ou appeler setValue plus tard déplace le composant aussi, et null le vide. Pour ne déplacer que le mois affiché, sans rien choisir, demandez-le au composant :
<tz-calendar #cal [(value)]="day" />
<button (click)="cal.goTo({ year: 2027, month: 3 })">Mars 2027</button>Formulaires
Tous sont des ControlValueAccessor : formControlName, ngModel et [(value)] fonctionnent.
<form [formGroup]="form">
<tz-datetime-field formControlName="at" timeZone="Europe/Paris" />
</form>Le contrôle contient des valeurs Temporal par défaut. valueAs change cela sans toucher à rien d’autre — c’est l’essentiel d’une migration depuis flatpickr sur la plupart des écrans :
<tz-date-field formControlName="day" valueAs="date" valueTimeZone="Europe/Paris" />
<tz-datetime-field formControlName="at" valueAs="iso" timeZone="Europe/Paris" />valueAs | Le contrôle contient |
|---|---|
'temporal' (défaut) | un PlainDate ou un Instant |
'utc' | une chaîne, toujours un instant : 2026-09-20T07:30:00Z — un champ de date seule rend le minuit qui ouvre le jour |
'date' | un Date — lu dans valueTimeZone, obligatoire et explicite, parce que le jour d’un instant dépend d’où l’on se tient |
'iso' | une chaîne : 2026-09-20, ou 2026-09-20T07:30:00Z |
Régler les conventions une fois
Un fuseau, une locale, la forme dans laquelle les valeurs sortent : ce sont des décisions qui concernent l’application, pas le champ. provideTzslot les pose pour tous les composants, et ce qui est écrit sur une balise l’emporte toujours.
import { provideTzslot, FR } from '@tzslot/angular';
bootstrapApplication(App, {
providers: [
provideTzslot({ valueAs: 'utc', timeZone: 'Europe/Paris', locale: 'fr-FR', messages: FR }),
],
});Le timeZone donné ici est celui que lisent tous les composants : il cesse donc d’être obligatoire sur la balise. Ce qu’on récupère explique les formes, et pourquoi les journées entières finissent au minuit suivant.
Les mots
Les messages viennent d’un seul endroit, anglais et français fournis :
import { provideTzslotMessages, FR } from '@tzslot/angular';
providers: [provideTzslotMessages(FR)];Chaque composant prend aussi messages sur la balise, ce qui l'emporte et ce dont a besoin un changement de langue à chaud : une valeur injectée est lue une seule fois, donc sans ça les noms de mois suivaient la bascule et les mots du composant restaient en place.
<tz-range-field [messages]="mots()" [locale]="langue()" />Zoneless
Rien dans ces composants n’a besoin de zone.js : ils lisent des signaux et écrivent dans le DOM. Ils fonctionnent aussi bien avec la détection de changement classique.