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

PropertyTypeDefaultDescription
ngModelModelSignal<string>'2001-01-01'Date value in YYYY-MM-DD format
validChangeOutputEmitter<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
  • ControlValueAccessor for forms

Base Class API (for Extension)

Key Members

MemberTypeDescription
ngModelModelSignal<string>Date value (YYYY-MM-DD)
validDatebooleanWhether current value is a valid date
validChangeOutputEmitter<boolean>Emits on validity change
d1/d2string (get/set)Day digit accessors (index 8, 9)
m1/m2string (get/set)Month digit accessors (index 5, 6)
y1/y2/y3/y4string (get/set)Year digit accessors (index 0-3)
moveTo()methodHandles keyup: validates digit, moves focus
select()methodSelects input content for overwrite
writeValue()methodControlValueAccessor — 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.