Date Edit
Digit-by-digit date input editor (DD-MM-RRRR format) with auto-navigation, validation, and ControlValueAccessor support.
This component has both a default concrete implementation (<smart-date-edit>) and an abstract base class for custom extensions.
Usage
<smart-date-edit
variant="standard"
[(ngModel)]="birthDate"
(validChange)="onValidChange($event)"
/>
<p [hidden]="isValid()">Enter a valid date of birth.</p>
Default Component Usage
Selector
<smart-date-edit>
Properties
| Property | Type | Default | Description |
|---|---|---|---|
ngModel | ModelSignal<string> | '2001-01-01' | Date value in YYYY-MM-DD format |
validChange | OutputEmitter<boolean> | - | Emits on validity change |
Usage
Both binding styles work, with or without FormsModule in the consuming component. <smart-date-edit> renders from an internal signal rather than straight off ngModel, so when FormsModule is imported and Angular's own NgModel directive also matches the element, the null it seeds its FormControl with cannot leak back into your bound property.
Features
- 8 individual digit inputs (DD + MM + YYYY)
- Auto-focus to next field on input
- Moment.js validation
- Invalid state: red border + red text
- Dark mode support
ControlValueAccessorfor forms
Base Class API (for Extension)
Key Members
| Member | Type | Description |
|---|---|---|
ngModel | ModelSignal<string> | Date value (YYYY-MM-DD) |
validDate | boolean | Whether current value is a valid date |
validChange | OutputEmitter<boolean> | Emits on validity change |
d1/d2 | string (get/set) | Day digit accessors (index 8, 9) |
m1/m2 | string (get/set) | Month digit accessors (index 5, 6) |
y1/y2/y3/y4 | string (get/set) | Year digit accessors (index 0-3) |
moveTo() | method | Handles keyup: validates digit, moves focus |
select() | method | Selects input content for overwrite |
writeValue() | method | ControlValueAccessor — sets value |
Extending the base class
import {
ChangeDetectionStrategy,
Component,
forwardRef,
signal,
ViewEncapsulation,
} from '@angular/core';
import { NG_VALUE_ACCESSOR } from '@angular/forms';
import { DateEditBaseComponent } from '@smartsoft001/angular';
/**
* A custom date editor built on `DateEditBaseComponent`.
*
* The base owns the `YYYY-MM-DD` value (`ngModel`), the per-digit accessors
* (`d1`, `m1`, `y1`, ...), the `validDate` flag and the whole
* `ControlValueAccessor` contract, so a custom implementation only has to
* provide a template and decide how a new value reaches the model. This one
* swaps the six digit boxes of the standard variation for one native picker.
*/
@Component({
selector: 'docs-custom-date-edit',
template: `
<label class="docs-date-edit">
<span class="docs-date-edit__label">Start date</span>
<input
class="docs-date-edit__input"
type="date"
[value]="ngModel()"
[attr.aria-invalid]="!validDate"
(change)="onPicked($event)"
/>
</label>
`,
encapsulation: ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush,
// Registering NG_VALUE_ACCESSOR is what makes the component usable with
// formControlName; the base already implements every method it needs.
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => CustomDateEditComponent),
multi: true,
},
],
})
export class CustomDateEditComponent extends DateEditBaseComponent {
onPicked(event: Event): void {
const value = (event.target as HTMLInputElement).value;
this.validDate = !!value;
// ngModel is a model() signal, so setting it also notifies the consumer.
this.ngModel.set(value);
this.propagateChange(this.validDate ? value : null);
this.propagateTouched();
this.validChange.emit(this.validDate);
}
}
/**
* Date edit has no injection token: `<smart-date-edit>` picks between its
* `standard` and `preset` variations through the `variant` input, so a custom
* implementation is rendered directly rather than registered.
*/
@Component({
selector: 'docs-date-edit-custom-example',
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [CustomDateEditComponent],
template: `
<docs-custom-date-edit
[ngModel]="date()"
(ngModelChange)="date.set($event)"
/>
<p class="docs-date-edit__value">Selected: {{ date() }}</p>
`,
})
export class DateEditCustomExampleComponent {
date = signal('2026-04-07');
}
DateEditPresetComponent (<smart-date-edit-preset>)
A fully-styled Preline single datepicker variation — a read-only trigger input plus a calendar popover. Drop-in replacement for the standard editor.
Unlike token-based components, <smart-date-edit> selects its variation through a variant input (NOT an InjectionToken / NgComponentOutlet). The wrapper dispatches via @switch (variant()) over DateEditVariantName ('standard' | 'preset'). Render the preset with:
The preset extends DateEditBaseComponent, so it shares ngModel (YYYY-MM-DD), validDate, and validChange. Preline's JS plugin is not installed, so the popover (open/close, prev/next month, day selection) is driven entirely by Angular signals — no external runtime. Month/year pickers are native <select> elements styled to match Preline.
Tailwind Classes
All classes use smart: prefix. Inputs: smart:w-8 smart:h-10 smart:text-center smart:border smart:rounded-md. Invalid: smart:border-red-500 smart:text-red-600. Dark: smart:dark:bg-gray-800 smart:dark:text-white.
Source
The component lives in packages/shared/angular/src/lib/components/date-edit and is documented for Claude Code by the angular-components-date-edit skill.