UNPKG

ngx-mask

Version:

Input masking for modern Angular — Reactive, template-driven and Signal Forms, zoneless and SSR ready. Dates, numbers, separators, custom patterns.

460 lines (331 loc) 13.1 kB
# ngx-mask > Input masking library for modern Angular. One standalone directive (`NgxMaskDirective`) and pipe (`NgxMaskPipe`) cover Reactive Forms, template-driven forms and Signal Forms through a `ControlValueAccessor`, and run in zoneless and SSR apps. No `NgxMaskModule` in current versions — configuration is registered via `provideEnvironmentNgxMask()` / `provideNgxMask()`. No runtime dependencies beyond Angular, ~15 KB gzipped. npm package: `ngx-mask`. Supports Angular 17+ (see version pins below for older Angular). Source: https://github.com/NepipenkoIgor/ngx-mask ## Installation ```bash npm install ngx-mask # or bun add ngx-mask ``` Older Angular versions need a pinned ngx-mask release: ```bash npm install ngx-mask@16.4.2 # Angular 16.x npm install ngx-mask@15.2.3 # Angular 15.x npm install ngx-mask@14.3.3 # Angular 14.x npm install ngx-mask@13.2.2 # Angular 13.x / 12.x ``` ## Setup: standalone application ```typescript // app.config.ts import { ApplicationConfig } from '@angular/core'; import { provideEnvironmentNgxMask } from 'ngx-mask'; export const appConfig: ApplicationConfig = { providers: [provideEnvironmentNgxMask()], }; ``` ```typescript // any component that uses the mask import { Component } from '@angular/core'; import { NgxMaskDirective } from 'ngx-mask'; @Component({ selector: 'app-example', standalone: true, imports: [NgxMaskDirective], template: `<input mask="0000" />`, }) export class ExampleComponent {} ``` With custom application-wide config: ```typescript import { NgxMaskConfig } from 'ngx-mask'; const maskConfig: Partial<NgxMaskConfig> = { validation: false }; bootstrapApplication(AppComponent, { providers: [provideEnvironmentNgxMask(maskConfig)] }).catch( (err) => console.error(err) ); ``` Per-component/feature override (replaces, does not merge with, the environment config for that subtree): ```typescript import { Component } from '@angular/core'; import { NgxMaskDirective, provideNgxMask } from 'ngx-mask'; @Component({ selector: 'my-feature', standalone: true, imports: [NgxMaskDirective], providers: [provideNgxMask({ thousandSeparator: ',' })], template: `<input mask="separator.2" />`, }) export class PriceInputComponent {} ``` Directive inputs (e.g. `[thousandSeparator]`) always win over `provideNgxMask()`, which always wins over `provideEnvironmentNgxMask()`, which wins over library defaults. ## Setup: NgModule-based application ```typescript import { NgModule } from '@angular/core'; import { NgxMaskDirective, NgxMaskPipe, provideEnvironmentNgxMask } from 'ngx-mask'; @NgModule({ imports: [NgxMaskDirective, NgxMaskPipe], exports: [NgxMaskDirective, NgxMaskPipe], providers: [provideEnvironmentNgxMask()], }) export class AppModule {} ``` Migrating from ngx-mask ≤ 14 (`NgxMaskModule` only exists there, for Angular < 15): ```typescript // Before (ngx-mask <= 14) @NgModule({ imports: [NgxMaskModule.forRoot(maskConfig)] }) export class AppModule {} // After (current ngx-mask) @NgModule({ imports: [NgxMaskDirective], providers: [provideEnvironmentNgxMask(maskConfig)], }) export class AppModule {} ``` ## Common pitfalls - `NullInjectorError: No provider for InjectionToken ngx-mask config` — the directive/pipe is used with no provider in scope. Add `provideEnvironmentNgxMask()` to bootstrap providers (or `provideNgxMask()` to the component). - `NgxMaskModule` not found — that API is ngx-mask ≤ 14 only. Use the standalone imports + provider functions above. - Config seems ignored — a closer `provideNgxMask()` in a parent component *replaces* (does not merge with) the environment config for that subtree; directive inputs override both. ## Basic usage: directive and pipe ```html <input type="text" mask="0000" /> <input type="text" [mask]="maskPattern" /> ``` ```html <span>{{ phone | mask: '(000) 000-0000' }}</span> <span>{{ value | mask: 'separator' : { thousandSeparator: ',', suffix: ' sm' } }}</span> ``` Built-in pattern tokens: | token | meaning | | ----- | ------- | | `0` | digit (0-9), required | | `9` | digit (0-9), optional | | `A` | letter or digit | | `S` | letter only | | `U` | uppercase letter only | | `L` | lowercase letter only | | mask example | matches | | ---------------- | -------------- | | `9999-99-99` | `2017-04-15` | | `0*.00` | `2017.22` | | `000.000.000-99` | `048.457.987-98` | | `AAAA` | `0F6g` | | `SSSS` | `asDF` | | `UUUU` | `ASDF` | | `LLLL` | `asdf` | ## Mask Options reference All options below are directive inputs (`<input [optionName]="value" mask="...">`) and are also accepted by `provideEnvironmentNgxMask(options)` / `provideNgxMask(options)` as an `NgxMaskOptions` object. ### specialCharacters (string[]) Default special characters: `- / ( ) . : (space) + , @ [ ] " '`. Overriding this array replaces the defaults entirely — list every character you need. ```html <input type="text" [specialCharacters]="['[', ']', '\\']" mask="[00]\[000]" /> ``` ```text Input value: 789-874.98 Masked value: [78]\[987] ``` ### patterns (`{ [char: string]: { pattern: RegExp, optional?: boolean } }`) ```html <input type="text" [patterns]="customPatterns" mask="(000-000)" /> ``` ```typescript public customPatterns = { '0': { pattern: new RegExp('[a-zA-Z]') } }; ``` ```text Input value: 789HelloWorld Masked value: (Hel-loW) ``` ### Custom pattern definition with a symbol ```typescript pattern = { B: { pattern: new RegExp('\\d'), symbol: 'X', }, }; ``` Reserved characters `h`, `d`, `m`, `s` are used by date/time patterns — avoid them in custom patterns. `*` is reserved for `0*`-style "any length of digits" masks. ### prefix (string) ```html <input type="text" prefix="+7" mask="(000) 000 00 00" /> ``` ### instantPrefix (boolean) Controls whether the prefix shows on an empty model. ```html <input type="text" prefix="+7" instantPrefix="false" mask="(000) 000 00 00" /> <input type="text" prefix="+7" instantPrefix="true" mask="(000) 000 00 00" /> ``` ### suffix (string) ```html <input type="text" suffix="$" mask="0000" /> ``` ### dropSpecialCharacters (boolean | string[]) Default `true` — special characters are stripped from the model value. ```html <input type="text" [dropSpecialCharacters]="false" mask="000-000.00" /> ``` ```text Input value: 789-874.98 Model value: 789-874.98 ``` ### showMaskTyped (boolean) Default `false` — show the mask skeleton while typing. ```html <input mask="(000) 000-0000" prefix="+7" [showMaskTyped]="true" /> ``` ### allowNegativeNumbers (boolean) Default `false`. ```html <input type="text" [allowNegativeNumbers]="true" mask="separator.2" /> ``` ```text Input value: -10,000.45 Model value: -10000.45 ``` ### placeHolderCharacter (string) Default `_`. Only relevant when `showMaskTyped` is `true`. ```html <input mask="(000) 000-0000" prefix="+7" [showMaskTyped]="true" placeHolderCharacter="*" /> ``` ### clearIfNotMatch (boolean) Default `false` — clears the input if the typed value does not fully match the mask. ### typeFromDecimals (boolean) Default `false`. Opt-in "banking"/calculator-style typing for `separator.N` masks (N > 0): digits fill from the decimal end (`5` -> `0.05`, then `7` -> `0.57`, then `3` -> `5.73`; backspace shifts back). Works with `thousandSeparator`, `prefix`/`suffix`, `allowNegativeNumbers`, `separatorLimit`. ```html <input type="text" mask="separator.2" [typeFromDecimals]="true" thousandSeparator="," /> <!-- typing 1 2 3 4 5 6 renders: 0.01 -> 0.12 -> 1.23 -> 12.34 -> 123.45 -> 1,234.56 --> ``` ```typescript provideNgxMask({ typeFromDecimals: true }); ``` ### defaultValueOnBlur (string) Default `null`. When set, this raw value is written through the mask pipeline on blur whenever the control's unmasked value is empty. ```html <input type="text" mask="separator.2" defaultValueOnBlur="0" /> <!-- user clears the input and blurs -> displayed "0", model 0 --> ``` With `showMaskTyped`: ```html <input type="text" mask="0000" [showMaskTyped]="true" defaultValueOnBlur="9" /> <!-- user blurs the empty input -> displayed "9___", model "9" --> ``` ```typescript provideNgxMask({ defaultValueOnBlur: '0' }); ``` For conditional/computed defaults, use transform functions instead: ```typescript public outputTransformFn = (value: string | number | undefined | null) => (value === '' || value == null ? 0 : value); public inputTransformFn = (value: unknown) => (value === '' || value == null ? '0' : (value as string | number)); ``` ### Pipe with a custom pattern: `[string, pattern]` ```html <span>{{ phone | mask: customMask }}</span> ``` ```typescript pattern = { P: { pattern: new RegExp('\\d') } }; customMask: [string, typeof pattern] = ['PPP-PPP', pattern]; ``` ### Repeat mask with `{n}` ```html <input type="text" mask="A{4}" /> ``` ### Thousand separator / decimal masks (`separator`) ```html <input type="text" mask="separator" /> <!-- default separator: space --> <input type="text" mask="separator" thousandSeparator="." /> <input type="text" mask="separator.2" /> <!-- .N = decimal precision; 2 is common for currency, 0 disables decimals --> ``` ```text Input: 1234.56 -> Masked: 1 234.56 (default space separator) Input: 1234,56 -> Masked: 1.234,56 (thousandSeparator=".") Input: 1234.56 -> Masked: 1,234 (thousandSeparator="," , separator.0) ``` ```html <input type="text" mask="separator.2" thousandSeparator="." /> <input type="text" mask="separator.2" thousandSeparator="," /> <input type="text" mask="separator.0" thousandSeparator="." /> <input type="text" mask="separator.0" thousandSeparator="," /> <input type="text" mask="separator.2" [leadZero]="true" /> <!-- Input: 12 -> Masked: 12.00 | Input: 12.1 -> Masked: 12.10 --> <input type="text" mask="separator.2" separatorLimit="1000" /> <!-- caps digits before the decimal point: Input 12345678,56 -> Masked 1.234,56 --> ``` On a `separator` mask with `decimalMarker=","`, the numeric-keypad decimal key inserts `,` while the main-keyboard `.` keeps its normal behavior. ### Time validation (24h) ```html <input type="text" mask="Hh:m0:s0" /> ``` ### Date validation ```html <input type="text" mask="d0/M0/0000" /> ``` ### leadZeroDateTime (boolean) Default `false`. Replaces skipped date/time digits with `0`. ```html <input type="text" mask="d0/M0/0000" [leadZeroDateTime]="true" /> <!-- Input: 422020 -> Masked: 04/02/2020 --> <input type="text" mask="Hh:m0:s0" [leadZeroDateTime]="true" /> <!-- Input: 777 -> Masked: 07:07:07 --> ``` ### Percent validation ```html <input type="text" mask="percent" suffix="%" /> ``` `percent` accepts multiple decimal markers via `[decimalMarker]="['.', ',']"` (both `12.5` and `12,5`). ### FormControl validation Default `true`. ```html <input type="text" mask="00 00" [validation]="true" /> ``` ### Secure / hidden input ```html <input placeholder="Secure input" [hiddenInput]="true" mask="XXX/X0/0000" /> ``` Date tokens `d` (day) and `M` (month) can be hidden too, keeping the year visible: ```html <input placeholder="Secure date input" [hiddenInput]="true" mask="d0/M0/0000" /> ``` ### Built-in validated masks: IP, CPF/CNPJ ```html <input mask="IP" /> <input mask="CPF_CNPJ" /> <input mask="CPF_CNPJ_ALPHA" /> ``` ### Multi-mask expressions with `||` ```html <input mask="000.000.000-00||00.000.000/0000-00" /> <input mask="(00) 0000-0000||(00) 0 0000-0000" /> <input mask="00||SS" /> ``` ### Custom mask aliases Define named masks once in config, reference by name in `mask`. Aliases resolve before other mask processing and may expand to a `||` multi-mask expression. ```typescript provideNgxMask({ maskAliases: { PHONE_BR: '(00) 00000-0000', MY_DOC: '000-AAA||0000-AAA', }, }); ``` ```html <input mask="PHONE_BR" /> <input mask="MY_DOC" /> ``` Alias keys must be UPPER_SNAKE_CASE and must not shadow built-in tokens (`IP`, `CPF_CNPJ`, `CPF_CNPJ_ALPHA`, ...) — a shadowing alias is ignored with a one-time console warning. The alias map is static per injector (no runtime changes). Security: alias values are mask expressions evaluated by the library — define them statically, never from untrusted user input. ### maskFilled output event ```html <input mask="0000" (maskFilled)="maskFilled()" /> ``` ## Version compatibility - Angular 17+: latest ngx-mask, full feature set (this doc). - Angular 16.x: `ngx-mask@16.4.2` - Angular 15.x: `ngx-mask@15.2.3` - Angular 14.x: `ngx-mask@14.3.3` - Angular 13.x / 12.x: `ngx-mask@13.2.2` - Only Angular 17+ builds receive new features and updates. ## Links - Live demo & interactive docs: https://nepipenkoigor.github.io/ngx-mask/ - Full USAGE reference: https://github.com/NepipenkoIgor/ngx-mask/blob/develop/USAGE.md - README: https://github.com/NepipenkoIgor/ngx-mask/blob/develop/README.md - Changelog: https://github.com/NepipenkoIgor/ngx-mask/blob/develop/CHANGELOG.md - Issues: https://github.com/NepipenkoIgor/ngx-mask/issues