Input
The <smart-input> component renders a form input for a single model field by delegating to a sub-component chosen by FieldType. It selects a sub-component based on the FieldType resolved from @Field() decorator metadata via an internal baseMap and an optional extendMap injected via INPUT_FIELD_COMPONENTS_TOKEN. Shows error messages via <smart-input-error> when the control is touched and invalid.
Usage
<smart-input [options]="options" />
Components
InputComponent (<smart-input>)
Main wrapper component. Resolves fieldOptions from @Field() decorator metadata on the model, then selects a sub-component from baseMap (or extendMap override) based on fieldOptions.type and renders it via NgComponentOutlet. Also renders an inline info button when fieldOptions.info is set and a loader while control.status === 'PENDING'. Below the input, renders <smart-input-error> when control.errors are present and the control has been touched.
InputBaseComponent (abstract)
Abstract base directive for all input sub-components. Provides:
options: InputSignal<InputOptions<T>>— field configuration (control, fieldKey, model, treeLevel, possibilities, component)fieldOptions: InputSignal<IFieldOptions | undefined>— resolved field decorator metadatacssClass: InputSignal<string>(aliasclass) — external CSS classesinternalOptions,control,required,possibilities— populated viaeffectwhen options changeafterSetOptionsHandler()— hook invoked after options resolve, used by sub-components for validator/possibilities setupformControl,formControlArray,formControlGroup— typed accessors forcontrol
InputPossibilitiesBaseComponent (abstract)
Extends InputBaseComponent for sub-components that need list possibilities (radio, check). Injects MODEL_POSSIBILITIES_PROVIDER and auto-refreshes possibilities on parent control changes.
Default Sub-Components
| FieldType | Sub-component | Selector | Description |
|---|---|---|---|
text | InputTextComponent | smart-input-text | Plain text input |
email | InputEmailComponent | smart-input-email | Email input |
password | InputPasswordComponent | smart-input-password | Password input + optional <smart-password-strength> |
nip | InputNipComponent | smart-input-nip | NIP with built-in invalidNip validator |
pesel | InputPeselComponent | smart-input-pesel | PESEL text input |
int | InputIntComponent | smart-input-int | Integer input (type="number" step="1") |
float | InputFloatComponent | smart-input-float | Decimal input (type="number" step="0.01") |
currency | InputCurrencyComponent | smart-input-currency | Currency input (type="number" step="0.01") |
phoneNumber | InputPhoneNumberComponent | smart-input-phone-number | Telephone input |
phoneNumberPl | InputPhoneNumberPlComponent | smart-input-phone-number-pl | Telephone input + built-in length(9) validator |
longText | InputLongTextComponent | smart-input-long-text | Rich text editor (ngx-editor) |
date | InputDateComponent | smart-input-date | HTML date input (type="date") |
dateWithEdit | InputDateWithEditComponent | smart-input-date-with-edit | Digit-by-digit date via <smart-date-edit> |
dateRange | InputDateRangeComponent | smart-input-date-range | Date range via <smart-date-range> |
flag | InputFlagComponent | smart-input-flag | Single checkbox |
radio | InputRadioComponent | smart-input-radio | Radio group (from possibilities) |
check | InputCheckComponent | smart-input-check | Checkbox group (from possibilities) |
enum | InputEnumComponent | smart-input-enum | Multi-select checkboxes from enum |
color | InputColorComponent | smart-input-color | Color picker (ngx-color-picker) |
logo | InputLogoComponent | smart-input-logo | Image upload with inline preview |
address | InputAddressComponent | smart-input-address | Nested FormGroup (city/zip/street/building/flat) |
object | InputObjectComponent | smart-input-object | Nested form via FORM_COMPONENT_TOKEN |
array | InputArrayComponent | smart-input-array | Dynamic array of nested forms |
ints | InputIntsComponent | smart-input-ints | Dynamic list of integer inputs |
strings | InputStringsComponent | smart-input-strings | Dynamic list of string inputs |
file | InputFileComponent | smart-input-file | Generic file chooser |
attachment | InputAttachmentComponent | smart-input-attachment | File upload (any type) with download/delete |
image | InputImageComponent | smart-input-image | Image upload with preview |
pdf | InputPdfComponent | smart-input-pdf | PDF upload with show/delete |
video | InputVideoComponent | smart-input-video | Video upload with play/delete |
API
InputComponent Inputs
| Input | Type | Default | Description |
|---|---|---|---|
options | InputSignal<InputOptions<T> | undefined> | optional | Field configuration |
InputOptions
Notes:
fieldKeyending withConfirmis stripped — confirm variants share the original field's@Field()metadata.mode==='create'or'update'merges the matching@Field({ create: … })/@Field({ update: … })override into the resolvedfieldOptions.- If
options.componentis provided, it overrides everything (bothbaseMapand token).
INPUT_FIELD_COMPONENTS_TOKEN
InjectionToken<Partial<Record<FieldTypeDef, Type<InputBaseComponent<any>>>>> — allows substituting a sub-component for any field type.
Maps are merged ({ ...baseMap, ...extendMap }), so only selected types need to be overridden.
Preline field presets
Each field type ships a Preline-styled preset (Input<Field>PresetComponent, selector smart-input-<field>-preset) alongside the default Tailwind-UI-styled field component. The presets live in <field>/preset/ and all translate the Preline look to smart:-prefixed vanilla Tailwind with explicit dark: variants. Apply them by providing the ready-made map INPUT_PRESET_FIELD_COMPONENTS for the token (it covers all field types except the standalone validation-message preset):
INPUT_PRESET_FIELD_COMPONENTS maps every FieldType that ships an input component (dateTime has no input component, so it has no map entry). The field-type presets and their selectors mirror the default components in the table above (smart-input-<field>-preset), including the most recently added ones:
| FieldType | Preset component | Selector | Look |
|---|---|---|---|
color | InputColorPresetComponent | smart-input-color-preset | Native color input in a rounded frame + swatch + hex |
object | InputObjectPresetComponent | smart-input-object-preset | Styled card frame around the nested form (unchanged nesting) |
address | InputAddressPresetComponent | smart-input-address-preset | Responsive grid of Preline text sub-inputs (street/no/zip/city) |
flag | InputFlagPresetComponent | smart-input-flag-preset | Preline checkbox (blue checked state) + inline label |
pdf | InputPdfPresetComponent | smart-input-pdf-preset | Dashed drop-zone (accept .pdf) + show/delete preview |
video | InputVideoPresetComponent | smart-input-video-preset | Dashed drop-zone (accept .mp4) + play/delete + <video> |
array | InputArrayPresetComponent | smart-input-array-preset | Item cards (space-y-2), outline add / ghost-red remove |
Notes:
- Interactive Preline widgets (enum select, password strength meter, date pickers, file drop-zone) are driven by Angular signals — the Preline JS plugins are not required.
- File-based presets (
pdf,video, plusfile/image/attachment) keep the hidden#inputObjfile input and reuseInputFileBaseComponent's upload/button wiring; the drop-zone and drag-and-drop are added on top. objectreusesFORM_COMPONENT_TOKENand theNgComponentOutlet/DynamicIoDirectivenesting of the default component — only the surrounding frame is styled, the nested rendering is untouched.arrayextends the concreteInputArrayComponent(add/remove/CVA logic untouched); each item is a card, the empty state renders an em dash, and both add and remove buttons hide whenfieldOptions().possibilities.staticis true.InputErrorPresetComponent(smart-input-error-preset) is a Preline-styled validation-message variant. BecauseInputComponenthard-codes<smart-input-error>(no token for it), use this preset via its selector directly — it is not part ofINPUT_PRESET_FIELD_COMPONENTS, and wiring it into the wrapper is out of scope.
Extending the base class
import {
ChangeDetectionStrategy,
Component,
computed,
ViewEncapsulation,
} from '@angular/core';
import {
ReactiveFormsModule,
UntypedFormControl,
Validators,
} from '@angular/forms';
import {
INPUT_FIELD_COMPONENTS_TOKEN,
InputBaseComponent,
InputComponent,
InputOptions,
ModelLabelPipe,
} from '@smartsoft001/angular';
import { Field, FieldType, Model } from '@smartsoft001/models';
@Model({ titleKey: 'nickname' })
export class DocsProfile {
@Field({ type: FieldType.text })
nickname = '';
}
@Component({
selector: 'docs-custom-input-text',
template: `
@if (control) {
<label class="docs-input__label">
{{
control?.parent?.value
| smartModelLabel
: internalOptions.fieldKey
: internalOptions?.model?.constructor
}}
@if (required) {
<span class="docs-input__required">*</span>
}
</label>
<input
type="text"
[class]="fieldClasses()"
[formControl]="formControl"
[attr.autofocus]="fieldOptions()?.focused ? true : null"
/>
}
`,
imports: [ReactiveFormsModule, ModelLabelPipe],
encapsulation: ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class CustomInputTextComponent<T> extends InputBaseComponent<T> {
// InputComponent forwards the external class under its aliased name, so the
// inherited `cssClass` input is used as is - do not redeclare it here.
fieldClasses = computed(() => {
const classes = ['docs-input__field'];
const extra = this.cssClass();
if (extra) classes.push(extra);
return classes.join(' ');
});
// `control`, `formControl`, `required`, `internalOptions` and
// `fieldOptions()` are all set up by InputBaseComponent from the options
// that `<smart-input>` forwards.
}
@Component({
selector: 'docs-input-custom-example',
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [InputComponent],
// Inputs are swapped per field type: every FieldType.text field below this
// component now renders CustomInputTextComponent instead of the built-in
// one. A single field can also be overridden with `options.component`.
providers: [
{
provide: INPUT_FIELD_COMPONENTS_TOKEN,
useValue: { [FieldType.text]: CustomInputTextComponent },
},
],
template: `<smart-input [options]="options" />`,
})
export class InputCustomExampleComponent {
control = new UntypedFormControl('', Validators.required);
// `<smart-input>` reads the @Field metadata of `model[fieldKey]` to decide
// which field component to render, so the model drives the dispatch.
options: InputOptions<DocsProfile> = {
control: this.control,
fieldKey: 'nickname',
model: new DocsProfile(),
treeLevel: 0,
};
}
Source
The component lives in packages/shared/angular/src/lib/components/input and is documented for Claude Code by the angular-components-input skill.