Angular Forms Library | ng-hub-ui-forms
Accessible Angular form fields (input, textarea, slider, select, datepicker) with automatic control/group/form error display, reactive forms and CSS variables.
API reference
Here's the full contract for forms: everything you can bind, listen to, project and theme, gathered in one place. Wire up what you need and style what you want — it's standalone and signal-friendly.
Inputs
Dial forms in with 87 inputs. Bind them like any Angular @Input.
| Name | Type | Default | Description |
|---|---|---|---|
formControlName / [formControl] | string | FormControl | — | Binds any field to a reactive control. Errors display automatically when the control is touched and invalid. |
label | string | '' | Field label text. |
labelType (hub-input, hub-select, hub-datepicker) | 'stacked' | 'floating' | 'horizontal' | 'stacked' | Label placement. `floating` lays the label inside the control, where it stands in for the placeholder until the field is focused or holds a value. |
labelType (hub-textarea, hub-otp-input, hub-slider, hub-segmented, hub-timepicker) | 'stacked' | 'horizontal' | 'stacked' | Label placement. These fields have no floating variant: passed `floating`, they render a stacked label. |
placeholder (hub-input, hub-select, hub-textarea, hub-datepicker) | string | '' / NgSelectConfig.placeholder (hub-select) | Placeholder text. |
formText | string | — | Helper text shown below the control. |
type (hub-input) — file is deprecated | 'text' | 'number' | 'password' | 'email' | 'tel' | 'url' | 'color' | 'checkbox' | 'switch' | 'counter' | 'file' | 'text' | The hub-input format. |
passwordRevealed (hub-input) | boolean | false | Two-way reveal state of a password field. Toggling the eye button updates it; setting it reveals or masks the value. |
passwordToggle (hub-input) | boolean | true | Whether the integrated reveal toggle is rendered on password fields. |
hideOnBlur (hub-input) | boolean | true | Whether a revealed password re-masks automatically when focus leaves the field. |
capsLockWarning (hub-input) | boolean | true | Whether a hint is shown under the field while Caps Lock is active. |
passwordStrength (hub-input) | boolean | false | Renders an opt-in 4-segment strength meter under the field. Scoring is overridable via provideHubForms. |
autocomplete (hub-input) | string | '' | Native autocomplete attribute, e.g. current-password or new-password for password managers. |
clearable (hub-input) | boolean | false | When true, shows an internal ✕ button once the field has a value; it resets the control and emits an empty search term. |
debounceTime (hub-input) | number | 0 | Debounce in milliseconds before the search event fires (0 = every keystroke). |
mask (hub-input) | string | — | Pattern mask for text formats. Tokens: 0 = digit, A = letter, * = alphanumeric; other chars are literal separators. E.g. "0000 0000 0000 0000". |
unmaskValue (hub-input) | boolean | false | With a mask set, store the raw typed characters (no separators) in the form instead of the formatted text. |
range (hub-slider) | boolean | false | Dual-thumb range slider; value becomes a [lower, upper] tuple. |
options (hub-segmented) | HubSegmentedOption[] | [] | The selectable options for hub-segmented, in render order — each { value, label, disabled? }. |
size (hub-segmented) | 'sm' | 'md' | 'lg' | 'md' | Visual density of the segmented control: 'sm', 'md' (default) or 'lg'. |
disabled (all fields) | boolean (two-way, model) | false | Disables the field. Inherited by every field from the shared base, and two-way — a field disabled through its reactive control reports the change back. On `hub-segmented`, individual options can also opt out via `option.disabled`. |
multiple (hub-segmented) | boolean | false | Allow selecting more than one option; the value becomes an array and options toggle (aria-pressed). |
vertical (hub-segmented) | boolean | false | Stack the segments vertically instead of in a horizontal row. |
vertical (hub-select) — deprecated | boolean | false | Deprecated, and tied to the deprecated non-dropdown formats: lays the buttons/checkbox/radio options out vertically. Use `<hub-segmented [vertical]="true">`. |
color (hub-segmented) | 'primary' | 'secondary' | 'success' | 'danger' | 'warning' | 'info' | 'neutral' | string | '' | Semantic accent for the selected segment (primary, success, …) or any custom colour; slides in single mode. |
value (hub-segmented) | unknown | unknown[] | undefined | Two-way bindable selected value (model()): the chosen option's value in single mode, an array of selected values in multiple mode. Emits valueChange; normally driven through formControlName instead. |
format (hub-select) — deprecated for buttons/checkbox/radio | 'dropdown' | 'buttons' | 'checkbox' | 'radio' | 'dropdown' | How the select renders the same value. |
prepend / append (hub-input, hub-select, hub-textarea, hub-datepicker, hub-timepicker) | string | string[] | '' | Static group addons on any field that renders as a box: a string is one addon, an array is a run of them, and empty entries are dropped rather than drawn as an empty box. The addon shares the field's border and is not focusable — for an interactive control attached to the edge, use the [hubPrepend] / [hubAppend] templates instead. Both can be present, and the projected content is always the outermost element on its side. |
items / bindLabel / bindValue / multiple (hub-select) | any[] / string / string / boolean | — | Options, the label/value property names for object items, and multi-select. |
groupBy (hub-select) | string | undefined | undefined | Property name (dot path) used to group the dropdown items under option-group headers. |
searchable / clearable (hub-select) | boolean | true | Whether the options can be filtered by typing (searchable) and whether the selection can be cleared with the built-in clear button (clearable). Both default to true. |
closeOnSelect (hub-select) | boolean | true | Whether the dropdown closes after selecting an item. Set to false to keep it open while picking several values in multiple mode. |
fixedPlaceholder (hub-select) | boolean | false | Keep the placeholder visible even when a value is selected. Defaults to false (the placeholder hides on selection), inverting ng-select v23's unconventional default. |
loading / notFoundText (hub-select) | boolean / string | false / NgSelectConfig.notFoundText | loading renders the dropdown's loading state while async items are being fetched; notFoundText is the message shown when no items match the search term. |
readonly (hub-input, hub-select, hub-textarea, hub-datepicker, hub-otp-input, hub-timepicker) | boolean | false | When true, the field displays its value but rejects any interaction, without the disabled styling or form-state change. |
appendTo (hub-select) | string | undefined | 'body' | CSS selector of the element the dropdown panel is appended to. Defaults to 'body' so the panel escapes overflow/transform ancestors (cards, scroll containers, modals); pass undefined to render it inline. |
addTag (hub-select) | boolean | ((term: string) => any | Promise<any>) | false | Create items from the search term (dropdown format): true adds the term as-is; a function maps the term to a new item, sync or Promise. |
addTagText (hub-select) | string | NgSelectConfig.addTagText | Label of the "add item" row shown while typing when addTag is enabled. |
minTermLength (hub-select) | number | 0 | Minimum search-term length before filtering (or the typeahead Subject) kicks in. |
typeahead (hub-select) | Subject<string> | undefined | undefined | Subject that receives search-term changes for async/server-side loading; when provided, client-side filtering switches off — feed the results back through items. |
compareWith (hub-select) | ((a: any, b: any) => boolean) | undefined | undefined | Custom equality between an item and the bound value (e.g. objects compared by id); when omitted, the vendor's built-in comparison applies. |
searchFn (hub-select) | ((term: string, item: any) => boolean) | undefined | undefined | Replaces the built-in matching used by `searchable`. Receives the typed term and an item, and returns whether the item matches — so a search can look at fields the label does not show. |
mode (hub-datepicker) | 'single' | 'range' | 'day-time-range' | 'single' | Single date, a free date range, or one day with a start and end time within it. |
granularity (hub-datepicker) | 'year' | 'month' | 'day' | 'hour' | 'minute' | 'second' | 'day' | Precision each picked point carries, and the panel that picks it: year and month render a 12-cell period grid, day the calendar, and hour, minute and second add a time strip to it. Orthogonal to mode — mode says how many points are picked, granularity how precise each one is. Defaults to day, which emits the same YYYY-MM-DD string as before; from hour onwards the value is a full ISO 8601 timestamp carrying the reader's local wall clock and the offset of that date. |
valueFormat / parse (hub-datepicker) | 'iso' | 'date' | 'timestamp' | ((date) => unknown) / ((raw) => Date | null) | null | 'iso' / null | The value axes. valueFormat sets what the bound control holds: 'iso' (the default), 'date' for a native Date, 'timestamp' for epoch milliseconds, or a function for anything else. parse sets how an incoming value is read, overriding the built-in detection of ISO strings of any width, Date instances and epoch milliseconds — and it applies to min and max too. |
minuteStep / secondStep / hourFormat (hub-datepicker) | number / number / '12' | '24' | undefined | 5 / 1 / undefined | Time controls: minuteStep and secondStep are the spinbutton increments, and hourFormat forces a 12- or 24-hour clock — derived from the locale when unset, and applied to the input display as well as to the panel, so the two can never disagree. All three fall back to the global provideHubForms config. |
min / max / disabledDates (hub-datepicker) | string | Date | number | null / ((date: Date) => boolean) | null | null | Selectable bounds and a per-date disabled predicate. The bounds honour the time, not just the day: a day is only disabled when no instant of it is allowed, so min = 2026-09-01T14:00 leaves 1 September clickable and the time controls refuse the earlier hours. Accepts an ISO string, a Date or epoch milliseconds. |
locale / firstDayOfWeek / weekdayFormat (hub-datepicker) | string / number / 'short' | 'narrow' | 'long' | LOCALE_ID / undefined / undefined | Calendar i18n: locale is the BCP-47 tag for the display value and month/weekday names (defaults to the app's LOCALE_ID, falling back to en-US); firstDayOfWeek sets the first weekday (0 = Sunday, 1 = Monday…); weekdayFormat picks the weekday header width. The last two fall back to the global provideHubForms config. |
displayFormat / timeDisplayFormat / rangeSeparator / labels (hub-datepicker) | Intl.DateTimeFormatOptions | string | ((date) => string) / Intl.DateTimeFormatOptions / string / Partial<HubDatepickerLabels> | undefined / undefined / undefined / {} | Display formatting: displayFormat renders the selected value and accepts Intl.DateTimeFormatOptions, an Angular date pattern such as 'dd/MM/yyyy HH:mm', or a function; timeDisplayFormat is composed over it once the granularity carries a time, so the date part keeps whatever displayFormat said; rangeSeparator is the text between the two dates of a range; and labels overrides the picker's UI labels per instance (merged over the global config). |
clearable / showToday / closeOnSelect (hub-datepicker) | boolean | true / true / true | Behavior flags: clearable shows the clear shortcut, showToday shows the "today" shortcut, and closeOnSelect closes the calendar after picking a date (single mode only). Defaults: true / true / true. |
multiple (hub-file-input) | boolean | false | Whether more than one file can be held. Switches the control value from File to File[]. |
accept (hub-file-input) | string | '*' | Accepted file types, e.g. image/*,.pdf. Enforced on drops and pastes, not just in the OS dialog. |
maxSize / minSize / maxTotalSize (hub-file-input) | number | null | null | Per-file maximum and minimum size, and the maximum combined size, in bytes. |
maxFiles (hub-file-input) | number | null | null | Maximum number of files. Only meaningful together with multiple. |
dragDrop / paste (hub-file-input) | boolean | true | Whether files can be dropped onto the field, and whether they can be pasted into it. |
preview (hub-file-input) | 'none' | 'list' | 'grid' | 'list' | How the held files are rendered: not at all, as a list, or as a thumbnail grid. |
allowDuplicates (hub-file-input) | boolean | false | Whether the same file can be selected twice (keyed on name, size and last-modified date). |
capture (hub-file-input) | 'user' | 'environment' | null | null | Opens the device camera instead of the file browser, on the platforms that support it. |
autoUpload (hub-file-input) | boolean | true | Whether a newly accepted file starts uploading immediately. Ignored when no uploader is registered. |
buttonLabel / hint (hub-file-input) | string | null | null | Overrides the browse-button text and the auto-generated constraints hint. |
dropText / dropSubtext (hub-file-input) | string | null | null | Per-instance overrides for the two invitation lines of the dropzone. Both fall back to the global labels. |
classlist (every field except hub-segmented and hub-legend) | string | '' | Extra CSS classes applied to the host element. |
showValid (all fields) | boolean | HubFormsConfig.showValid | Whether a valid field shows its valid state. Off by default because a form that paints every untouched field green says nothing; the invalid state is always shown. |
validFeedback (all fields) | string | null | null | Message rendered under the field once it is valid, when `showValid` is on. |
invalidFeedbackTemplateFn (all fields) | ((key: string, value: any) => string) | null | null | Overrides the message for a given error key on this field alone. Receives the error key and its value, and returns the text — the app-wide default lives in `provideHubForms`. |
min / max (hub-input) | number | undefined (two-way, model) | undefined | Numeric bounds for the number and counter formats. Two-way, so a counter that clamps its own value reports the bound back. |
mode (hub-otp-input) | 'numeric' | 'alphanumeric' | 'alpha' | 'numeric' | Which characters a box accepts, and which keyboard a phone offers: digits only, or letters and digits. |
min / max (hub-timepicker) | string (HH:MM) | '' | Earliest and latest time the field accepts, as `HH:MM`. Empty means unbounded. |
step (hub-timepicker) | number (seconds) | 0 | Granularity of the native stepper, in seconds. `0` leaves the browser default of one minute. |
group / groupName (form[hubForm], hub-fieldset) | AbstractControl | null / string | undefined | null / undefined | The group whose errors the container surfaces. Give `group` an `AbstractControl` directly, or `groupName` the name of a child of the surrounding form — neither is needed when the container already wraps the group it reports on. |
errorTrigger (form[hubForm], hub-fieldset) | 'touched' | 'submit' | 'always' | 'touched' | When group-level errors become visible: once the group has been touched, only after a submit attempt, or immediately. |
formTextType (all fields) | 'bottom' | — | Helper text placement. Only `bottom` is supported. |
rows (hub-textarea) | number | — | Number of visible text rows. |
cols (hub-textarea) | number | — | Number of visible columns. When set, the textarea stops stretching to full width. |
maxlength (hub-textarea) | number | — | Maximum number of characters. Drives both the native limit and the counter. |
counter (hub-textarea) | boolean | — | Whether to show a character counter below the control. |
autoresize (hub-textarea) | boolean | — | Whether the textarea grows to fit its content. |
length (hub-otp-input) | number | — | Number of cells / characters in the code. |
secret (hub-otp-input) | boolean | — | Masks the characters like a password. |
separator (hub-otp-input) | string | — | The separator character shown between groups. |
separatorEvery (hub-otp-input) | number | — | Inserts a visual separator every N cells (0 = none). |
showValue (hub-slider) | boolean | — | Whether to show the value bubble(s) above the thumb(s). |
step (hub-input, hub-slider) | number | — | Step value (numeric / counter inputs). |
legend (hub-fieldset) | string | — | Legend text. Ignored when a `hubLegend` template is projected. |
required (all fields) | boolean | null (two-way, model) | null | Whether the field is required. With a reactive binding it is derived from the control’s own validators, so setting it inline is only honoured for template-driven fields; it drives the asterisk and the `required` attribute. |
required (hub-legend) | boolean | — | Whether to render the required indicator. |
invalid (hub-legend) | boolean | — | Whether to render the invalid state styling. |
Outputs
React to what forms does — 13 events to hook your logic onto.
| Name | Type | Description |
|---|---|---|
valueChange | EventEmitter<value> | Emits whenever the field value changes. |
search (hub-input) | EventEmitter<string> | Debounced typeahead term emitted as the user types (text-like inputs). |
rejected (hub-file-input) | EventEmitter<HubFileRejection[]> | The files refused by the declared constraints, each with the reason for its rejection. |
fileRemoved (hub-file-input) | EventEmitter<File> | The file removed from the selection. |
uploadStateChange (hub-file-input) | EventEmitter<readonly HubFileItem[]> | The full item list whenever an upload changes status or progress. |
submit (form[hubForm]) — the native event | SubmitEvent (native, not a custom output) | Not a custom output: `hubForm` listens to the form’s own `submit` event, prevents the default navigation, marks the tree as touched and flips the submitted state. Bind `(submit)` on the form for your own handler. |
disabledChange / requiredChange (all fields) · labelTypeChange / placeholderChange / minChange / maxChange / passwordRevealedChange (hub-input) · valueChange (hub-segmented) | EventEmitter<T> (the paired half of each two-way model) | Every two-way `model()` input publishes a matching `Change` output, so `[(disabled)]` and friends work. They are listed here because the banana-in-a-box syntax hides them. |
opened / closed / cleared / viewChange (hub-datepicker) | EventEmitter | Calendar lifecycle and navigation events. |
onOpen / onClose / onClear / onSearch / onAdd / onRemove (hub-select) | EventEmitter | Dropdown lifecycle and tag/typeahead events. |
onFocus / onBlur (hub-select) | EventEmitter<any> | Emit when the dropdown control gains or loses focus (dropdown format), relaying the vendor's focus/blur events. |
scroll / scrollToEnd (hub-select) | EventEmitter<{ start: number; end: number }> / EventEmitter<any> | scroll emits the visible { start, end } window while the dropdown list scrolls; scrollToEnd emits when the list reaches its end — the hook for infinite/paged loading with typeahead. |
enter (hub-input) | EventEmitter<number | string | boolean | File | FileList | null> | Emits the current value when Enter is pressed. |
completed (hub-otp-input) | EventEmitter<string> | Emits the full code once every cell is filled. |
Templates
Make it yours — 10 template slots let you project custom markup.
| Name | Description | Example |
|---|---|---|
hubFormText | Project rich helper-text content. | <ng-template hubFormText>…</ng-template> |
hubValidationError | Override the message for a given error key. | <ng-template hubValidationError key="min">…</ng-template> |
hubLegend | Custom fieldset legend content. | <ng-template hubLegend>…</ng-template> |
ng-option-tmp (hub-select) | Custom dropdown option rendering (re-exported ng-select directive). | <ng-template ng-option-tmp let-item="item">…</ng-template> |
hubFileIcon (hub-file-input) | Per-file glyph for files with no thumbnail; receives the item as implicit context. | <ng-template hubFileIcon let-item>…</ng-template> |
hubFilePreview (hub-file-input) | Replaces the whole rendering of a selected file, receiving the item plus the remove, retry and cancel actions. | <ng-template hubFilePreview let-item let-remove="remove">…</ng-template> |
hubFileDropzoneNotice (hub-file-input) | Projects arbitrary markup at the top of the dropzone, above the glyph — for a notice the invitation and the constraints hint cannot express. | <p hubFileDropzoneNotice>2 documents still missing</p> |
hubSegmentedOption (hub-segmented) | Replaces each segment's content (icons, badges, rich markup) while the component keeps owning selection, keyboard navigation and ARIA. Context: the option (implicit), selected and index. | <ng-template hubSegmentedOption let-option let-selected="selected" let-index="index">…</ng-template> |
hubSelectSuffix (hub-select, deprecated) | Deprecated since 22.16.0 — use [hubAppend], which does the same on every field. It keeps working; [hubAppend] wins if both are present. Attaches an interactive control to the select's inline-end edge — a button that acts on whatever is selected: configure it, look it up, create a new one. Declared as a template rather than projected content because the select's catch-all ng-content carries ng-option through to the engine and would swallow it; rendering from a template also keeps the action after the control in the DOM, so tabbing reaches the field before the button acting on it. The control and the action share one border and read as a single field. | <ng-template hubSelectSuffix><button …>…</button></ng-template> |
hubPrepend / hubAppend (all box-shaped fields) | Content attached to a field's edge, richer than the text a prepend / append string can carry: an icon, or a button that acts on the value. Available on every field that renders as a box with a value — hub-input, hub-select, hub-textarea and hub-datepicker. Composes with the string addons rather than replacing them: the strings render first, so projected content is always the outermost element on its side — a unit labels the field, the action sits beyond it. Whatever is projected wears the field's border, radius and height rather than its own, so a button does not draw a second, thicker seam beside the control. Declared as a template because a field's ng-content is already spoken for. A slot can also hand over another field — hub-input, hub-select, hub-textarea or hub-datepicker as a direct child — which closes flush against the host so that a price and the period it is a price of read as the one statement they are. | <ng-template hubAppend><button …><hub-icon … /></button></ng-template> |