CngxInputMask
projects/forms/input/input-mask.directive.ts
Import#
import { CngxInputMask } from '@cngx/forms/input'
Description#
Pattern-based input mask directive.
Supports custom patterns, locale-aware presets, multiple patterns (separated by |),
prefix/suffix, custom tokens, and character transforms.
Mask tokens
| Token | Description | Regex |
|---|---|---|
0 |
Required digit | [0-9] |
9 |
Optional digit | [0-9]? |
A |
Required letter | [a-zA-Z] |
a |
Optional letter | [a-zA-Z]? |
* |
Required alphanumeric | [a-zA-Z0-9] |
\\ |
Escape next char as literal | - |
Built-in presets
Pass a preset name instead of a pattern. Region suffix optional (defaults to LOCALE_ID).
| Preset | Example | Notes |
|---|---|---|
date |
cngxInputMask="date" |
Locale-aware DD/MM/YYYY or MM/DD/YYYY |
date:short |
cngxInputMask="date:short" |
2-digit year |
time / time:24 |
cngxInputMask="time" |
HH:MM (24h) |
time:12 |
cngxInputMask="time:12" |
HH:MM AM/PM |
datetime |
cngxInputMask="datetime" |
Date + time |
phone |
cngxInputMask="phone:CH" |
Country-specific |
creditcard |
cngxInputMask="creditcard" |
Amex/Visa/MC auto-switch |
iban |
cngxInputMask="iban:CH" |
Country-specific grouping |
zip |
cngxInputMask="zip:DE" |
Country-specific |
ip / ipv4 |
cngxInputMask="ip" |
099.099.099.099 |
mac |
cngxInputMask="mac" |
AA:AA:AA:AA:AA:AA |
Multiple patterns
Separate patterns with | - the directive selects the best match based on input length:
<input cngxInputMask="(00) 0000-0000|(00) 00000-0000" /><!-- Phone (locale-aware) -->
<input cngxInputMask="phone" />
<!-- Date (locale-aware) -->
<input cngxInputMask="date" />
<!-- Custom pattern with prefix -->
<input cngxInputMask="000.000.000-00" [prefix]="'CPF: '" />
<!-- Custom token: hex color -->
<input cngxInputMask="\\#HHHHHH" [customTokens]="{ H: { pattern: /[0-9a-fA-F]/, transform: c => c.toUpperCase() } }" />
<!-- Credit card with auto-format switching -->
<input cngxInputMask="creditcard" />Metadata#
Host#
Providers#
CNGX_VALUE_TRANSFORMER- useFactory
(dir: CngxInputMask): CngxValueTransformerdeps:=> ({ format: (raw: string) => dir.toMaskedDisplay(raw), parse: (display: string) => dir.fromMaskedDisplay(display), }) forwardRef(() => CngxInputMask)
Relationships
Index#
Properties
Methods
Outputs
Derived State
HostBindings
Inputs#
Whether to clear the input when focus is lost and the mask is incomplete.
falsenumber | nullForce the Nth |-alternate, bypassing the length-based pick. Clamped to
the available range; null (default) restores length-based selection.
Changing it never clears value - the auto-clear watches mask(), which
this leaves untouched.
nullboolean | undefinedWhether to guide cursor to the next empty position. Falls back to global config.
Mask pattern, preset name, or |-separated patterns.
{ alias: 'cngxInputMask' }string | undefinedPlaceholder char shown for unfilled positions. Falls back to global config.
Static text prepended to the display value (not part of mask or raw value).
''Static text appended to the display value (not part of mask or raw value).
''Outputs#
Instance Properties#
unknownThe currently active pattern (useful when using multi-pattern or presets).
this.activePatternunknownTransform function applied to each accepted character before insertion.
input<((char: string) => string) | undefined>(undefined)Methods#
HostBindings#
| Binding | Expression |
|---|---|
[attr.aria-placeholder] | ariaPlaceholder() |
HostListeners#
| Event | Handler |
|---|---|
(beforeinput) | beforeinput() |
(keydown) | keydown() |
(mousedown) | mousedown() |
(focus) | focus() |
(mouseup) | mouseup() |
(blur) | blur() |
(paste) | paste() |
All presets
import { ChangeDetectionStrategy, Component, LOCALE_ID } from '@angular/core';
import { CngxInputMask } from '@cngx/forms/input';
/**
* The full CngxInputMask preset spectrum on one page.
*
* Each field uses a built-in preset string - no custom token map needed. The
* locale-aware presets (`date`, `datetime`) follow the `en-US` `LOCALE_ID`
* provided below; switch it to `de-DE` to see the separators flip. Region
* presets (`phone:DE`, `iban:CH`, `zip:GB`) lazy-load their table on first
* use, so they settle a tick after render; the static presets (`time`,
* `creditcard`, `ip`, `mac`) and custom token patterns render immediately.
*/
@Component({
selector: 'app-root',
standalone: true,
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [CngxInputMask],
// en-US drives the locale-aware date/datetime separators. Try 'de-DE'.
providers: [{ provide: LOCALE_ID, useValue: 'en-US' }],
template: `
<div style="display:grid;gap:0.85rem;max-width:30rem">
@for (f of fields; track f.mask) {
<label style="display:grid;gap:0.25rem">
<span style="font-size:0.8125rem;font-weight:600">{{ f.label }}</span>
<input
[cngxInputMask]="f.mask"
[prefix]="f.prefix ?? ''"
#m="cngxInputMask"
style="font:inherit;padding:0.4rem 0.55rem;border:1px solid #cbd5e1;border-radius:0.375rem"
/>
<small style="opacity:0.65">
<code>{{ f.mask }}</code> · raw: {{ m.value() || '(empty)' }}
</small>
</label>
}
</div>
`,
})
export class AllPresetsExample {
protected readonly fields: ReadonlyArray<{ label: string; mask: string; prefix?: string }> = [
{ label: 'Date (locale)', mask: 'date' },
{ label: 'Date + time', mask: 'datetime' },
{ label: 'Time (24h)', mask: 'time' },
{ label: 'Credit card (Amex/Visa auto-switch)', mask: 'creditcard' },
{ label: 'Phone (Germany)', mask: 'phone:DE' },
{ label: 'IBAN (Switzerland)', mask: 'iban:CH' },
{ label: 'Postal code (UK)', mask: 'zip:GB' },
{ label: 'IPv4 address', mask: 'ip' },
{ label: 'MAC address', mask: 'mac' },
{ label: 'Custom pattern + prefix', mask: 'AA 000 000', prefix: 'CH-' },
];
}
export { AllPresetsExample as AppComponent };
Build your own pattern
import { ChangeDetectionStrategy, Component, signal } from '@angular/core';
import { CngxInputMask } from '@cngx/forms/input';
/**
* Build your own mask pattern live.
*
* Type a pattern into the top field and the masked input below re-targets to
* it through `[cngxInputMask]="pattern()"`. Tokens: `0` digit, `A` letter,
* `*` alphanumeric, `9` optional digit, `a` optional letter; any other
* character is a literal, and `\\` escapes a token char into a literal.
* Changing the pattern clears the entered value - that is the directive's
* documented auto-clear on a mask-string change.
*/
@Component({
selector: 'app-root',
standalone: true,
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [CngxInputMask],
template: `
<div style="display:grid;gap:0.85rem;max-width:30rem">
<label style="display:grid;gap:0.25rem">
<span style="font-size:0.8125rem;font-weight:600">Mask pattern</span>
<input
[value]="pattern()"
(input)="pattern.set(asValue($event))"
spellcheck="false"
style="font:inherit;padding:0.4rem 0.55rem;border:1px solid #cbd5e1;border-radius:0.375rem"
/>
</label>
<label style="display:grid;gap:0.25rem">
<span style="font-size:0.8125rem;font-weight:600">Your masked input</span>
<input
[cngxInputMask]="pattern()"
#m="cngxInputMask"
style="font:inherit;padding:0.4rem 0.55rem;border:1px solid #cbd5e1;border-radius:0.375rem"
/>
</label>
<small style="opacity:0.7">Display: {{ m.maskedValue() }} · raw: {{ m.value() || '(empty)' }}</small>
<fieldset style="border:1px solid #e2e8f0;border-radius:0.5rem;padding:0.6rem 0.75rem;display:grid;gap:0.4rem">
<legend style="font-size:0.8125rem;font-weight:600;padding:0 0.3rem">Tokens</legend>
@for (t of tokens; track t.token) {
<div style="display:flex;gap:0.5rem;font-size:0.8125rem">
<code style="min-width:1.5rem">{{ t.token }}</code>
<span style="opacity:0.75">{{ t.meaning }}</span>
<button
type="button"
(click)="append(t.token)"
style="margin-left:auto;font:inherit;font-size:0.75rem;padding:0.1rem 0.45rem;border:1px solid #cbd5e1;border-radius:0.3rem;background:#f8fafc;cursor:pointer"
>
add
</button>
</div>
}
</fieldset>
<div style="display:flex;gap:0.4rem;flex-wrap:wrap">
@for (p of presets; track p) {
<button
type="button"
(click)="pattern.set(p)"
style="font:inherit;font-size:0.75rem;padding:0.2rem 0.55rem;border:1px solid #cbd5e1;border-radius:0.3rem;background:#f8fafc;cursor:pointer"
>
{{ p }}
</button>
}
</div>
</div>
`,
})
export class PatternBuilderExample {
protected readonly pattern = signal('(000) 000-0000');
protected readonly tokens: ReadonlyArray<{ token: string; meaning: string }> = [
{ token: '0', meaning: 'required digit' },
{ token: 'A', meaning: 'required letter' },
{ token: '*', meaning: 'letter or digit' },
{ token: '9', meaning: 'optional digit' },
{ token: 'a', meaning: 'optional letter' },
];
protected readonly presets: readonly string[] = [
'(000) 000-0000',
'0000 0000 0000 0000',
'AA-00-AA',
'00:00',
];
protected asValue(event: Event): string {
return (event.target as HTMLInputElement).value;
}
protected append(token: string): void {
this.pattern.update((p) => p + token);
}
}
export { PatternBuilderExample as AppComponent };