ngx-mask
Version:
Input masking for modern Angular — Reactive, template-driven and Signal Forms, zoneless and SSR ready. Dates, numbers, separators, custom patterns.
798 lines • 38.8 kB
TypeScript
import * as i0 from "@angular/core";
import { ElementRef, EnvironmentProviders, EventEmitter, InjectionToken, OnChanges, PipeTransform, Provider, SimpleChanges } from "@angular/core";
import { ControlValueAccessor, FormControl, ValidationErrors, Validator } from "@angular/forms";
import { FormValueControl } from "@angular/forms/signals";
export type InputTransformFn = (value: unknown) => string | number;
export type OutputTransformFn = (value: string | number | undefined | null) => unknown;
/** Single decimal marker character accepted when `decimalMarker` is given as an array. */
export type DecimalMarkerChar = '.' | ',';
export type NgxMaskConfig = {
suffix: string;
prefix: string;
thousandSeparator: string;
decimalMarker: DecimalMarkerChar | [DecimalMarkerChar, DecimalMarkerChar];
clearIfNotMatch: boolean;
showMaskTyped: boolean;
placeHolderCharacter: string;
shownMaskExpression: string;
specialCharacters: string[] | readonly string[];
dropSpecialCharacters: boolean | string[] | readonly string[];
hiddenInput: boolean;
validation: boolean;
instantPrefix: boolean;
separatorLimit: string;
apm: boolean;
allowNegativeNumbers: boolean;
leadZeroDateTime: boolean;
leadZero: boolean;
/**
* Opt-in "banking" typing mode for separator masks with a fixed precision
* (`separator.N`, N > 0): typed digits fill the value from the decimal end,
* ATM/calculator style (5 -> 0.05 -> 0.57 -> 5.73); backspace shifts digits
* back to the right. Pasted and model-written values keep the regular
* separator formatting. Config-only option (provideNgxMask / pipe config) —
* it has no directive input.
*/
typeFromDecimals: boolean;
triggerOnMaskChange: boolean;
keepCharacterPositions: boolean;
inputTransformFn: InputTransformFn;
outputTransformFn: OutputTransformFn;
maskFilled: EventEmitter<void>;
/**
* User-defined named mask aliases resolved at the DI-config level (static per injector).
* When the `mask` input (or pipe mask argument) exactly matches an alias key, the aliased
* expression is substituted before any other mask processing (including `||` multi-masks).
* Alias keys must not shadow built-in tokens (IP, CPF_CNPJ, CPF_CNPJ_ALPHA, ...) — such
* aliases are ignored with a one-time console warning.
*/
maskAliases: Record<string, string>;
/**
* When set, this raw value is written through the regular mask pipeline on blur
* whenever the control's unmasked value is empty (covers '', a bare prefix/suffix and
* the showMaskTyped skeleton): the display shows the masked default and the model
* receives the usual output (dropSpecialCharacters/outputTransformFn applied). The
* write keeps the control's pristine state. `null` (default) keeps current behavior.
*/
defaultValueOnBlur: string | null;
patterns: Record<string, {
pattern: RegExp;
optional?: boolean;
symbol?: string;
}>;
};
export type NgxMaskOptions = Partial<NgxMaskConfig>;
export declare const NGX_MASK_CONFIG: InjectionToken<NgxMaskConfig>;
export declare const NEW_CONFIG: InjectionToken<NgxMaskConfig>;
export declare const INITIAL_CONFIG: InjectionToken<NgxMaskConfig>;
export declare const initialConfig: NgxMaskConfig;
/**
* Resolves a user-defined mask alias to its mask expression. Returns the input expression
* unchanged when no alias matches or when the alias key shadows a built-in token (in which
* case a console warning is emitted once per key and the built-in wins).
*/
export declare function resolveMaskAlias(maskExpression: string | null | undefined, maskAliases: Record<string, string> | undefined): string;
export declare const timeMasks: string[];
export declare const withoutValidation: string[];
export declare function provideNgxMask(configValue?: NgxMaskOptions | (() => NgxMaskOptions)): Provider[];
export declare function provideEnvironmentNgxMask(configValue?: NgxMaskOptions | (() => NgxMaskOptions)): EnvironmentProviders;
declare class NgxMaskApplierService {
protected _config: NgxMaskConfig;
dropSpecialCharacters: NgxMaskConfig['dropSpecialCharacters'];
hiddenInput: NgxMaskConfig['hiddenInput'];
clearIfNotMatch: NgxMaskConfig['clearIfNotMatch'];
specialCharacters: NgxMaskConfig['specialCharacters'];
patterns: NgxMaskConfig['patterns'];
prefix: NgxMaskConfig['prefix'];
suffix: NgxMaskConfig['suffix'];
private _thousandSeparator;
/**
* The separator between thousand groups. One containing a digit is ignored entirely (no
* grouping, nothing stripped): it could not be told from the number's own digits, and grouping
* with it would re-match its own output forever.
*/
get thousandSeparator(): NgxMaskConfig['thousandSeparator'];
set thousandSeparator(value: NgxMaskConfig['thousandSeparator']);
decimalMarker: NgxMaskConfig['decimalMarker'];
showMaskTyped: NgxMaskConfig['showMaskTyped'];
placeHolderCharacter: NgxMaskConfig['placeHolderCharacter'];
validation: NgxMaskConfig['validation'];
separatorLimit: NgxMaskConfig['separatorLimit'];
allowNegativeNumbers: NgxMaskConfig['allowNegativeNumbers'];
leadZeroDateTime: NgxMaskConfig['leadZeroDateTime'];
leadZero: NgxMaskConfig['leadZero'];
typeFromDecimals: NgxMaskConfig['typeFromDecimals'];
apm: NgxMaskConfig['apm'];
inputTransformFn: NgxMaskConfig['inputTransformFn'] | null;
outputTransformFn: NgxMaskConfig['outputTransformFn'] | null;
keepCharacterPositions: NgxMaskConfig['keepCharacterPositions'];
instantPrefix: NgxMaskConfig['instantPrefix'];
triggerOnMaskChange: NgxMaskConfig['triggerOnMaskChange'];
protected _shift: Set<number>;
plusOnePosition: boolean;
maskExpression: string;
actualValue: string;
shownMaskExpression: NgxMaskConfig['shownMaskExpression'];
deletedSpecialCharacter: boolean;
/**
* Whether we are currently in writeValue function, in this case when applying the mask we don't want to trigger onChange function,
* since writeValue should be a one way only process of writing the DOM value based on the Angular model value.
*/
writingValue: boolean;
ipError?: boolean;
cpfCnpjError?: boolean;
applyMask(inputValue: string | object | boolean | null | undefined, maskExpression: string, position?: number, justPasted?: boolean, backspaced?: boolean, cb?: (actualShift: number, backspaceShift: boolean) => void): string;
_findDropSpecialChar(inputSymbol: string): undefined | string;
_findSpecialChar(inputSymbol: string): undefined | string;
_checkSymbolMask(inputSymbol: string, maskSymbol: string): boolean;
/**
* `RegExp.prototype.test` is stateful for patterns carrying the `g` or `y` flag (it starts
* at `lastIndex`), so a user pattern such as `/[0-9]/g` would alternate between accepting
* and rejecting identical input. Every pattern test goes through here to start from 0.
*/
protected _testPattern(pattern: RegExp, value: string): boolean;
protected _formatWithSeparators: (str: string, thousandSeparatorChar: string, decimalChars: string | string[], precision: number) => string;
/**
* Formats a value for the `typeFromDecimals` mode: every digit of the raw value is
* read as one integer that is then split `precision` digits from the right
* (ATM/calculator style). Non-digit characters are ignored, so plain typing,
* mid-string edits and backspace all reduce to "digits shifted through the
* decimal marker".
*/
protected _formatFromDecimals(value: string, precision: number, decimalMarker: string): string;
/** Inserts `separator` between every 3-digit group of an integer-digit string. */
private _applyThousandGrouping;
protected percentage: (str: string) => boolean;
getPrecision: (maskExpression: string) => number;
private checkAndRemoveSuffix;
protected checkInputPrecision: (inputValue: string, precision: number) => string;
protected _stripToDecimal(str: string): string;
protected _charToRegExpExpression(char: string): string;
protected _shiftStep(cursor: number): void;
protected _compareOrIncludes<T>(value: T, comparedValue: T | T[], excludedValue: T): boolean;
protected _validIP(valuesIP: string[]): boolean;
/**
* The single rule for "which decimal marker is active": the configured marker, or — for an
* array — the first configured marker present in `value` that is not the thousandSeparator,
* else the first configured marker that is not the thousandSeparator (#1653).
*/
protected _activeDecimalMarker(value: string): DecimalMarkerChar;
protected _splitPercentZero(value: string): string;
protected _findFirstNonZeroAndDecimalIndex(inputString: string, decimalMarker: '.' | ','): {
decimalMarkerIndex: number | null;
nonZeroIndex: number | null;
};
static ɵfac: i0.ɵɵFactoryDeclaration<NgxMaskApplierService, never>;
static ɵprov: i0.ɵɵInjectableDeclaration<any>;
}
export declare class NgxMaskService extends NgxMaskApplierService {
isNumberValue: boolean;
maskIsShown: string;
selStart: number | null;
selEnd: number | null;
maskChanged: boolean;
previousValue: string;
currentValue: string;
isInitialized: boolean;
/**
* Set by the directive's keepCharacterPositions handling for the current edit:
* true — the directive fully resolved the resulting display value into actualValue,
* so applyMask must short-circuit and render actualValue as-is;
* false — the edit must flow through regular masking (no short-circuit);
* null — the directive was not involved in this applyMask call (legacy behavior).
* Consumed and reset by applyMask. This lets keepCharacterPositions work without
* showMaskTyped (#1545, #1543).
*/
keepCharacterPositionsHandled: boolean | null;
_isFocused: import("@angular/core").WritableSignal<boolean>;
private _emitValue;
onChange: (_: unknown) => void;
readonly _elementRef: ElementRef<any> | null;
private readonly document;
protected _config: NgxMaskConfig;
private readonly _renderer;
/**
* Applies the mask to the input value.
* @param inputValue The input value to be masked.
* @param maskExpression The mask expression to apply.
* @param position The position in the input value.
* @param justPasted Whether the value was just pasted.
* @param backspaced Whether the value was backspaced.
* @param cb Callback function.
* @returns The masked value.
*/
applyMask(inputValue: string, maskExpression: string, position?: number, justPasted?: boolean, backspaced?: boolean, cb?: (actualShift: number, backspaceShift: boolean) => void): string;
private _numberSkipedSymbols;
applyValueChanges(position: number, justPasted: boolean, backspaced: boolean, cb?: (actualShift: number, backspaceShift: boolean) => void): void;
hideInput(inputValue: string, maskExpression: string): string;
getActualValue(res: string): string;
shiftTypedSymbols(inputValue: string): string;
/**
* Convert number value to string
* 3.1415 -> '3.1415'
* 1e-7 -> '0.0000001'
*/
numberToString(value: number | string): string;
/**
* Locale-independent replacement for toLocaleString('fullwide', { useGrouping: false,
* maximumFractionDigits: 20 }) (#1573): expands exponential notation ('7e-7', '1e+21')
* to plain decimal form using '.' as decimal marker, regardless of the runtime locale.
*/
private _toPlainDecimalString;
showMaskInInput(inputVal?: string): string;
clearIfNotMatchFn(): void;
set formElementProperty([name, value]: [string, string | boolean]);
checkDropSpecialCharAmount(mask: string): number;
/**
* Consumes a pending `maskChanged` once a render has handled the reconfiguration, unless
* `triggerOnMaskChange` still needs it to emit the re-masked value (formControlResult clears
* it then).
*/
_clearMaskChangedUnlessTriggered(): void;
removeMask(inputValue: string): string;
private _checkForIp;
private _checkForCpfCnpj;
/** Collects the characters counted as "typed" for CPF/CNPJ progress tracking: digits only
* for the numeric mask, alphanumerics for CPF_CNPJ_ALPHA. */
private _countCpfCnpjTypedChars;
/**
* Recursively determine the current active element by navigating the Shadow DOM until the Active Element is found.
*/
_getActiveElement(document?: DocumentOrShadowRoot): Element | null;
/**
* Propogates the input value back to the Angular model by triggering the onChange function. It won't do this if writingValue
* is true. If that is true it means we are currently in the writeValue function, which is supposed to only update the actual
* DOM element based on the Angular model value. It should be a one way process, i.e. writeValue should not be modifying the Angular
* model value too. Therefore, we don't trigger onChange in this scenario.
* @param inputValue the current form input value
*/
formControlResult(inputValue: string): void;
private _toNumber;
private _removeMask;
private _removePrefix;
private _removeSuffix;
private _retrieveSeparatorValue;
private _regExpForRemove;
private _replaceDecimalMarkerToDot;
_checkSymbols(result: string): string | number | undefined | null;
private _checkPatternForSpace;
private _retrieveSeparatorPrecision;
_checkPrecision(separatorExpression: string, separatorValue: string): number | string;
/**
* True when the plain decimal string carries more significant digits than an IEEE-754
* double can represent exactly (15 is the guaranteed round-trip digit count), meaning a
* Number() round-trip would corrupt it (#1567).
*/
private _exceedsDoublePrecision;
/**
* Exact string-based equivalent of Number.prototype.toFixed (round half away from zero)
* for plain decimal strings beyond double precision (#1567).
*/
private _stringToFixed;
_repeatPatternSymbols(maskExp: string): string;
/**
* Decimal marker of the value being normalized in writeValue/pipe flows.
*
* #1573: this used to return the RUNTIME default locale's decimal marker
* ((1.1).toLocaleString().substring(1, 2)), which made mask behavior depend on the
* OS/browser regional format: under a comma-decimal locale (e.g. Edge + Austrian
* regional settings) a preformatted value like '10,000' (thousandSeparator ',')
* had its ',' replaced by the configured '.' decimalMarker, corrupting the value
* 1000x. JS number stringification (String(n)) always uses '.', and string values
* are expected to use the configured markers — the runtime locale is never the
* right source, so this is always '.'.
*/
currentLocaleDecimalMarker(): string;
static ɵfac: i0.ɵɵFactoryDeclaration<NgxMaskService, never>;
static ɵprov: i0.ɵɵInjectableDeclaration<any>;
}
export declare class NgxMaskDirective implements ControlValueAccessor, OnChanges, Validator, FormValueControl<string | number | null | undefined> {
mask: import("@angular/core").InputSignal<string | null | undefined>;
specialCharacters: import("@angular/core").InputSignal<string[] | readonly string[]>;
patterns: import("@angular/core").InputSignal<Record<string, {
pattern: RegExp;
optional?: boolean;
symbol?: string;
}>>;
prefix: import("@angular/core").InputSignal<string>;
suffix: import("@angular/core").InputSignal<string>;
thousandSeparator: import("@angular/core").InputSignal<string>;
decimalMarker: import("@angular/core").InputSignal<import("ngx-mask").DecimalMarkerChar | [import("ngx-mask").DecimalMarkerChar, import("ngx-mask").DecimalMarkerChar]>;
dropSpecialCharacters: import("@angular/core").InputSignal<boolean | string[] | readonly string[] | null>;
hiddenInput: import("@angular/core").InputSignal<boolean | null>;
showMaskTyped: import("@angular/core").InputSignal<boolean | null>;
placeHolderCharacter: import("@angular/core").InputSignal<string | null>;
shownMaskExpression: import("@angular/core").InputSignal<string | null>;
clearIfNotMatch: import("@angular/core").InputSignal<boolean | null>;
validation: import("@angular/core").InputSignal<boolean | null>;
separatorLimit: import("@angular/core").InputSignal<string | null>;
typeFromDecimals: import("@angular/core").InputSignal<boolean | null>;
allowNegativeNumbers: import("@angular/core").InputSignal<boolean | null>;
leadZeroDateTime: import("@angular/core").InputSignal<boolean | null>;
leadZero: import("@angular/core").InputSignal<boolean | null>;
triggerOnMaskChange: import("@angular/core").InputSignal<boolean | null>;
apm: import("@angular/core").InputSignal<boolean | null>;
inputTransformFn: import("@angular/core").InputSignal<import("ngx-mask").InputTransformFn | null>;
outputTransformFn: import("@angular/core").InputSignal<import("ngx-mask").OutputTransformFn | null>;
keepCharacterPositions: import("@angular/core").InputSignal<boolean | null>;
instantPrefix: import("@angular/core").InputSignal<boolean | null>;
defaultValueOnBlur: import("@angular/core").InputSignal<string | null>;
/**
* Signal Forms `FormValueControl` surface. At runtime `FormField` never drives this model
* when the directive is bound (it prefers the `NG_VALUE_ACCESSOR` provided above, see
* `_isCvaMode`), but the template type-checker treats any directive owning a `value` model
* as the field's custom control and checks the bound field's value type against it (#1640).
* The model therefore accepts exactly what `writeValue()` accepts — `string | number | null |
* undefined` — so numeric and nullable fields (`signal({ amount: 0 })`,
* `signal<number | null>(null)`, `signal<string | null>(null)`) compile under
* `strictTemplates`. Values pushed back into the model are always the unmasked string (see
* `_propagateToValueModel`), i.e. `value()` / `valueChange` are declared wider than what is
* emitted.
*/
value: import("@angular/core").ModelSignal<string | number | null | undefined>;
disabled: import("@angular/core").InputSignalWithTransform<boolean, unknown>;
touched: import("@angular/core").ModelSignal<boolean>;
maskFilled: import("@angular/core").OutputEmitterRef<void>;
private _maskValue;
private _inputValue;
private _code;
private _maskExpressionArray;
private _justPasted;
/** For IME composition event */
private _isComposing;
/**
* True once Angular has driven this directive through the `ControlValueAccessor` contract
* (`registerOnChange`). NOTE: Signal Forms' `FormField` ALSO takes this path — it prefers a
* host-provided `NG_VALUE_ACCESSOR` over the custom-control `value` model binding (see
* `FormField.ɵngControlCreate`), so it calls `registerOnChange`/`writeValue` too and this
* flag is `true` in both modes. That is fine: with the flag set, the `value`-model effect
* below is a no-op and all rendering goes through `writeValue()`. The `value` model is only
* driven directly (flag stays `false`) when the directive is used standalone with a
* `[(value)]` binding and no forms integration.
*/
private _isCvaMode;
/** Guards against the value effect echoing back a value we just propagated ourselves. */
private _skipNextValueEffect;
/**
* The exact stringified value last pushed through `onChange` (view → model). Signal Forms'
* `FormField` echoes every model update back through `writeValue()` — including updates that
* originated from the view. Re-masking that unmasked echo is at best a redundant re-render
* and at worst destructive for masks whose unmasked form is ambiguous (e.g. IP:
* '192168178' cannot reconstruct the typed dots of '192.168.1.78'). `writeValue()` consumes
* this marker to skip exactly that echo; ngOnChanges reads it to recognise a `[value]`
* binding echo of it (#1671). `null` = no pending propagation.
*/
private _lastPropagatedValue;
/**
* The raw control value `writeValue` is currently applying, `null` outside a write. The mask's own
* `onChange` echo is skipped when it carries exactly this value (the mask left it unchanged);
* an echo that differs (a normalised or re-typed value) still reaches the form.
*/
private _writtenValue;
/**
* Counts the values put into the input: each `writeValue` that reached the rendering step and
* each user edit. A write whose masked echo makes a `valueChanges` subscriber call `setValue`
* again is superseded by that nested write; the outer one compares this counter and leaves the
* DOM to the latest write (#1691). The deferred leadZero pass of a write compares it as well, so
* it never re-applies the written value over a newer write or a user edit.
*/
private _writeSequence;
/**
* True while the directive renders a value the user did not type: a `writeValue`, its deferred
* leadZero pass, a separator change re-rendering the number, a default applied on blur. A model
* update the mask derives from such a write goes straight to the control without dirtying it
* (see `_emitProgrammaticValue`).
*/
private _programmaticWrite;
/**
* The value the edit currently handled by `onInput` pushed through `onChange`, `null` while it
* pushed nothing. Read right after the edit to decide `maskFilled`.
*/
private _valuePropagatedByEdit;
/**
* True once the first `ngOnChanges` pass has applied the mask configuration to the service.
* Signal Forms' `FormField` syncs its field value through the template `ɵɵcontrol` update
* instruction, which runs BEFORE the sibling directives' first `ngOnChanges` on the same
* element — so the very first `writeValue()` would otherwise see an unconfigured service
* (empty `maskExpression`, default `leadZero`/`thousandSeparator`/...) and render the raw
* value. Until this flag is set, `writeValue()` stashes the incoming value and `ngOnChanges`
* replays it once the configuration is in place.
*/
private _configApplied;
private _pendingInitialValue;
private _hasPendingInitialValue;
/**
* True once the `disabled` input has ever delivered `true`. The disabled effect's very
* first run fires with the input's default `false` even when nothing binds `[disabled]`.
* Because effects run after Angular Forms' `setUpControl` (which calls
* `setDisabledState(true)` for initially-disabled controls) and both DOM writes are
* queueMicrotask-deferred in FIFO order, forwarding that default `false` would land last
* and re-enable an initially-disabled control (#1607, #1614). An initial `false` write is
* never needed — inputs are enabled by default — so `false` is only forwarded after the
* input has explicitly driven the state to `true` at least once.
*/
private _disabledEverSet;
/** Ensures the multi-character placeHolderCharacter warning (#1347) is emitted only once. */
private _warnedAboutMultiCharPlaceholder;
_maskService: NgxMaskService;
protected _config: NgxMaskConfig;
/**
* `specialCharacters` and `decimalMarker` as bound (the config values when unbound). The
* service derives working values from them for the current configuration (the minus
* `allowNegativeNumbers` keeps, the markers a separator mask drops, the comma a dot
* thousandSeparator forces), so a runtime reconfiguration starts again from these instead of
* piling the previous configuration's reductions up.
*/
private readonly _boundConfig;
/**
* Under zoneless change detection, writing to the underlying FormControl programmatically
* (e.g. `setValue`/`patchValue` from outside a signal/effect context, or from a callback
* zone.js used to auto-flush like a deferred render callback or `queueMicrotask`) does not itself
* trigger a CD run. Bindings that read `form.pristine`/`form.dirty`/`form.value` on the host
* template need an explicit `markForCheck()` once the directive finishes reacting to a
* programmatic value write, otherwise the view stays stale until something else schedules CD.
*/
private readonly _changeDetectorRef;
private readonly _elementRef;
private readonly _renderer;
private readonly _injector;
private readonly _ngZone;
private _ngControl;
/**
* The nearest NgControl: the element's own, else the one bound to an enclosing custom control.
* Only read from the `registerOnChange` callback, i.e. once a control was handed this
* directive's accessor contract — directly, or forwarded by a wrapper whose control is then
* the nearest one. A wrapper that implements the contract itself never registers with the
* mask, so the mask never writes to that wrapper's control.
*/
private _resolveNgControl;
onChange: (_: unknown) => void;
onTouch: () => void;
private _onValidatorChange;
constructor();
ngOnChanges(changes: SimpleChanges): void;
/** With `allowNegativeNumbers` the minus is part of the number, never a character to drop. */
private _keepMinusOutOfSpecialCharacters;
validate({ value }: FormControl): ValidationErrors | null;
/**
* The validation verdict for `value`. `filled` is true when the value passed every check and
* reached the end of the rules: a non-empty value that satisfies the mask. `maskFilled` is
* derived from it after a user edit; the validator itself must stay side-effect free.
*/
private _validateValue;
onPaste(): void;
onFocus(): void;
onModelChange(value: unknown): void;
onInput(e: Event): void;
/** `insertText`-style input event carrying more than one character (composition excluded). */
private _isBulkInsertion;
/**
* Whether nothing the user entered follows the caret, i.e. the edit ended the value. Only
* the suffix and the untouched skeleton of a showMaskTyped / keepCharacterPositions field
* (special and placeholder characters) may still follow it.
*/
private _editEndsValue;
private _processInput;
/**
* Whether any pattern slot of the current mask expression can accept a letter.
* IME composition is only meaningful for such masks; for purely numeric masks
* (digit patterns, separator, date/time, IP, CPF_CNPJ) waiting for compositionend
* only delays the model sync — and Samsung Keyboard may never fire it until blur
* (#1293). Unknown/letterless probe failures fall back to `false` (process live).
*/
private _maskAcceptsLetterInput;
onCompositionStart(): void;
onCompositionEnd(e: Event): void;
onBlur(e: Event): void;
/** The blur-time leadZero padding and clearIfNotMatch pass. */
private _formatOnBlur;
/**
* Issue #1435 (defaultValueOnBlur): when configured — via the directive input or the
* DI config — and the control's unmasked value is empty on blur (covers '', a bare
* prefix/suffix and the showMaskTyped skeleton), writes the default through the
* regular mask pipeline: applyMask renders the masked display and emits the usual
* model output (dropSpecialCharacters/outputTransformFn applied) via formControlResult.
* The default is no user edit, so it does not dirty the control. Returns whether a default
* was applied.
*/
private _applyDefaultValueOnBlur;
onClick(e: Event): void;
onKeyDown(event: Event): void;
/**
* #1641: on layouts whose decimal separator is a comma, the numeric keypad still emits
* a period, which a separator mask configured with a comma decimal marker rejects. The
* keypad decimal key (`key: 'Decimal'`, or a `.` from `code: 'NumpadDecimal'`) is
* therefore turned into the configured marker. Only the keypad key is intercepted: a
* period typed on the main keyboard keeps its normal behavior (e.g. as a thousand
* separator). Returns true when the key press was handled.
*/
private _insertNumpadDecimalMarker;
/**
* Runs `write` as a programmatic write: the model updates it causes do not dirty the control
* (see `_programmaticWrite`). Nested writes keep the flag of the enclosing one.
*/
private _writeProgrammatically;
/**
* Passes a model value the mask derived from a programmatic write on to the control, without
* dirtying it.
*
* When the directive is the bound control's own accessor, `fn` is Angular's `updateControl`
* callback: the value goes the way its view-change pipeline takes it (`setValue` without a
* view echo, then `viewToModelUpdate`, which emits `ngModelChange`), minus `markAsDirty()`,
* so the control and its ancestors keep the dirty and touched state they had. In every other
* case (a wrapper forwarding the accessor, which transforms the value or reacts to it
* itself; Signal Forms; no control) `fn` is the contract the caller registered and is called;
* the nearest control then gets back the pristine and untouched state it had before the call.
*/
private _emitProgrammaticValue;
/** It writes the value in the input */
writeValue(controlValue: unknown): void;
/**
* Mirrors a writeValue-driven render into the DOM synchronously, in the same
* change-detection pass (#1305). The service's `formElementProperty` setter defers all
* writes via queueMicrotask (to keep FIFO ordering with config-driven re-renders and
* dodge ExpressionChanged issues), but consumers that read `nativeElement.value` DURING
* the CD pass — Angular Material's floating label (`MatInput.empty`), CDK autofill —
* never see a value that only lands in a later microtask. The deferred write still runs
* afterwards and re-applies the same final value, so ordering guarantees are preserved.
*
* Skipped while a mask reconfiguration is pending (`mask()` input changed but
* `ngOnChanges` has not applied it yet — e.g. `mask.set(...)` + `setValue(...)` before
* the next CD pass): the value just computed used the STALE mask config, and rendering
* it synchronously would expose an intermediate state that the deferred pipeline is
* about to supersede. Multi-masks (`||`) resolve `_maskValue` to one alternative and
* therefore also fall back to the deferred-only path.
*/
private _writeElementValueSync;
/** The `mask` input with a user-defined alias (config maskAliases) expanded, if any. */
private _resolvedMaskInput;
registerOnChange(fn: typeof this.onChange): void;
registerOnValidatorChange(fn: () => void): void;
registerOnTouched(fn: typeof this.onTouch): void;
/**
* Pushes the current unmasked value into the `value` model input. In Signal Forms mode this
* fires the `valueChange` output that Angular listens to; in CVA mode it is a harmless write
* to a model nobody reads. We flag `_skipNextValueEffect` so the resulting model change does
* not bounce back through the value effect and overwrite the raw `_inputValue`.
*/
private _propagateToValueModel;
/**
* Whether the `value`-model effect will render `value` itself via writeValue(): only in
* standalone `[(value)]` / Signal Forms mode (Angular's writeValue owns rendering in CVA mode), not for
* the model echo of our own onChange propagation (`_skipNextValueEffect`), and only when it
* differs from what is already rendered. Shared with ngOnChanges (#1671), which must leave
* exactly that render to the effect.
*/
private _valueEffectWillWrite;
/**
* Focus the input element.
* Required by FormValueControl interface for Signal Forms.
*/
focus(): void;
checkSelectionOnDeletion(el: HTMLInputElement): void;
/**
* It disables the input element.
*
* Mirrors `_writeElementValueSync` (#1305): the service's `formElementProperty` setter
* defers ALL DOM writes via `queueMicrotask` to dodge ExpressionChangedAfterItHasBeenChecked
* and keep FIFO ordering with config-driven re-renders. For `disabled` specifically, that
* deferral leaves `nativeElement.disabled` stale for at least one microtask after Angular
* Forms' `setUpControl` calls this method synchronously during init — long enough for a
* consumer reading the DOM property in the same synchronous phase (or another deferred write
* racing in FIFO order) to observe the wrong value, e.g. an initially-disabled FormControl
* whose native input briefly (or, depending on interleaving, persistently) reports
* `disabled === false` (#1633). Writing synchronously here closes that gap; the deferred
* write below still runs afterwards and re-applies the same final value, preserving the
* existing ordering guarantees other call sites rely on.
*/
setDisabledState(isDisabled: boolean): void;
private _mirrorInput;
private _applyMask;
/**
* Renders `inputValue` through the mask, falling back to the raw value verbatim when the
* mask cannot process ANY of it (#1615, e.g. a sentinel like 'ONGOING' written into a
* digits-only control). Values that PARTIALLY match keep regular masking.
*
* Shared by writeValue() and _applyMask() (called from every ngOnChanges pass, including
* ones triggered by an UNRELATED input like `disabled`) so the verbatim verdict for a
* value written once via writeValue() is not lost on a later re-render that replays the
* same raw `inputValue` outside of writeValue — which would otherwise re-run regular
* masking, produce an empty result, and emit it through onChange, clobbering the model.
*
* Excludes an actual mask RECONFIGURATION (`maskChanged`): when the mask itself just
* changed, a value that no longer matches must clear through the regular path (see
* trigger-on-mask-change.spec.ts) — verbatim passthrough only covers re-renders of the
* SAME mask.
*/
private _maskedOrVerbatim;
private _validateTime;
private _getActualInputLength;
/**
* For `||` multi-masks only (#1583): a value shorter than the currently selected
* alternative is still valid when it is a pattern-valid prefix of that alternative
* ending exactly at a special-character boundary (e.g. `0` for `0,N`) and it meets
* the length requirement of at least one alternative (e.g. `1`). Values stopping
* mid-pattern-block (e.g. `112A` for `000SS`) remain invalid.
*/
private _isCompleteAlternativeBoundary;
/**
* True when every character of the mask expression is either a pattern token or a
* special character — i.e. the mask has no quantifiers (`*`, `?`), curly-bracket
* repetitions or other constructs the position-aware matcher does not model.
*/
private _isPlainTokenMask;
/**
* Backtracking match of a value against a mask mixing optional and mandatory pattern
* tokens (#1515, e.g. `999SSS`). Optional tokens may be left unfilled; special
* characters may be absent from the value (dropSpecialCharacters). The value is valid
* when it is fully consumed and every remaining mask token is optional or special.
*/
private _matchesMaskWithOptionalSkip;
private _createValidationError;
private _setMask;
private _areAllCharactersInEachStringSame;
static ɵfac: i0.ɵɵFactoryDeclaration<NgxMaskDirective, never>;
static ɵdir: i0.ɵɵDirectiveDeclaration<NgxMaskDirective, "input[mask], textarea[mask]", ["mask", "ngxMask"], {
"mask": {
"alias": "mask";
"required": false;
"isSignal": true;
};
"specialCharacters": {
"alias": "specialCharacters";
"required": false;
"isSignal": true;
};
"patterns": {
"alias": "patterns";
"required": false;
"isSignal": true;
};
"prefix": {
"alias": "prefix";
"required": false;
"isSignal": true;
};
"suffix": {
"alias": "suffix";
"required": false;
"isSignal": true;
};
"thousandSeparator": {
"alias": "thousandSeparator";
"required": false;
"isSignal": true;
};
"decimalMarker": {
"alias": "decimalMarker";
"required": false;
"isSignal": true;
};
"dropSpecialCharacters": {
"alias": "dropSpecialCharacters";
"required": false;
"isSignal": true;
};
"hiddenInput": {
"alias": "hiddenInput";
"required": false;
"isSignal": true;
};
"showMaskTyped": {
"alias": "showMaskTyped";
"required": false;
"isSignal": true;
};
"placeHolderCharacter": {
"alias": "placeHolderCharacter";
"required": false;
"isSignal": true;
};
"shownMaskExpression": {
"alias": "shownMaskExpression";
"required": false;
"isSignal": true;
};
"clearIfNotMatch": {
"alias": "clearIfNotMatch";
"required": false;
"isSignal": true;
};
"validation": {
"alias": "validation";
"required": false;
"isSignal": true;
};
"separatorLimit": {
"alias": "separatorLimit";
"required": false;
"isSignal": true;
};
"typeFromDecimals": {
"alias": "typeFromDecimals";
"required": false;
"isSignal": true;
};
"allowNegativeNumbers": {
"alias": "allowNegativeNumbers";
"required": false;
"isSignal": true;
};
"leadZeroDateTime": {
"alias": "leadZeroDateTime";
"required": false;
"isSignal": true;
};
"leadZero": {
"alias": "leadZero";
"required": false;
"isSignal": true;
};
"triggerOnMaskChange": {
"alias": "triggerOnMaskChange";
"required": false;
"isSignal": true;
};
"apm": {
"alias": "apm";
"required": false;
"isSignal": true;
};
"inputTransformFn": {
"alias": "inputTransformFn";
"required": false;
"isSignal": true;
};
"outputTransformFn": {
"alias": "outputTransformFn";
"required": false;
"isSignal": true;
};
"keepCharacterPositions": {
"alias": "keepCharacterPositions";
"required": false;
"isSignal": true;
};
"instantPrefix": {
"alias": "instantPrefix";
"required": false;
"isSignal": true;
};
"defaultValueOnBlur": {
"alias": "defaultValueOnBlur";
"required": false;
"isSignal": true;
};
"value": {
"alias": "value";
"required": false;
"isSignal": true;
};
"disabled": {
"alias": "disabled";
"required": false;
"isSignal": true;
};
"touched": {
"alias": "touched";
"required": false;
"isSignal": true;
};
}, {
"value": "valueChange";
"touched": "touchedChange";
"maskFilled": "maskFilled";
}, never, never, true, never>;
}
export declare class NgxMaskPipe implements PipeTransform {
private readonly defaultOptions;
private readonly _environmentInjector;
private _maskService;
private _maskExpressionArray;
private mask;
transform(value: string | number, mask: string, { patterns, maskAliases, ...config }?: Partial<NgxMaskConfig>): string;
private _setMask;
static ɵfac: i0.ɵɵFactoryDeclaration<NgxMaskPipe, never>;
static ɵpipe: i0.ɵɵPipeDeclaration<NgxMaskPipe, "mask", true>;
}
export {};