Используя реактивные формы в Angular, я создал две директивы: одну, которая преобразует значения из центов в доллары путем деления на 100, а другую, которая преобразует доллары в центы путем умножения на 100. Это работает, когда я вручную устанавливаю элементы управления формой, но не получается. применил OnInit, и он не применяется, когда ввод формы отображается, когда он скрыт за *ngIf.
Я создал stackblitz с примером. В OnInit должно отображаться $12,345.50
, но вместо этого отображается $12,34550.00
и одно и то же значение, когда отображается поле формы, пока они не будут установлены вручную с помощью кнопок. Пример приведен в Angular v15, но я использую v18, если это помогает с решением, но могу воспроизвести его аналогичным образом в любом из них.
Есть ли способ запустить директивы OnInit и при отображении ввода? Или мне следует реализовать это по-другому?
Мы можем получить доступ к директиве NgControl
и напрямую исправить преобразованное значение при первой загрузке.
Также не используйте *ngIf
при работе со скрытыми формами. Из-за этого элементы управления не работают должным образом, поскольку они удалены из DOM; вместо этого всегда используйте атрибут [hidden]
:
import { AfterContentInit, Directive, Input, inject } from '@angular/core';
import {
DefaultValueAccessor,
FormControlName,
NgControl,
} from '@angular/forms';
import {
maskitoNumberOptionsGenerator,
maskitoParseNumber,
} from '@maskito/kit';
export const dollarMask = maskitoNumberOptionsGenerator({
min: 0,
// Maximum to avoid floating point precision errors
// which will occur when numeric values are too large
// exceeding Number.MAX_SAFE_INTEGER
max: 1000000000,
precision: 2,
decimalSeparator: '.',
decimalZeroPadding: true,
thousandSeparator: ',',
prefix: '$',
});
@Directive({
standalone: true,
selector: '[maskito][toDollars]',
})
export class ToDollarsDirective {
private readonly accessor = inject(DefaultValueAccessor, { self: true });
private readonly formControlName = inject(NgControl, { self: true });
ngOnInit() {
const initialValue = this.formControlName.value;
const dollars1 = initialValue != null ? initialValue / 100 : initialValue;
this.formControlName.control!.patchValue(dollars1);
}
ngOnAfterContentInit() {
const original = this.accessor.writeValue.bind(this.accessor);
this.accessor.writeValue = (value: any) => {
const dollars = value != null ? value / 100 : value;
original(dollars);
};
}
}
@Directive({
standalone: true,
selector: '[maskito][toCents]',
})
export class ToCentsDirective {
private readonly accessor = inject(DefaultValueAccessor, { self: true });
private readonly formControlName = inject(NgControl, { self: true });
ngAfterContentInit() {
const original = this.accessor.onChange.bind(this.accessor);
this.accessor.onChange = (value: any) => {
const cents = maskitoParseNumber(value) * 100;
original(cents);
};
}
}
Спасибо, это было очень полезно! Я заметил, что директива toCents
, похоже, влияет на проверку ввода. Можно ли что-то добавить в директиву, чтобы это не мешало проверкам?
«Интерфейс OnInit имеет метод-перехватчик с именем ngOnInit(). Если вы реализуете этот метод в своем классе компонента или директивы, Angular вызывает его вскоре после первой проверки входных свойств для этого компонента или директивы». Итак, в вашей директиве реализуйте OnInit, и они будут вызваны в это время.