Я создал валютную директиву, которую я буду использовать в каждом элементе ввода, которая нуждается в валютном формате.
Итак, у меня есть 2 Host Listener, один OnFocus, второй Blur.
И это работает отлично. Но мне нужно отформатировать значение ввода, когда я устанавливаю значение ввода путем привязки
Поэтому, когда я открываю модальное окно, я получаю неформатированное значение... NgOnInit не работает, потому что слишком рано поднимается.
Вот мой код директивы.
import { Directive, HostListener, Input, OnInit, ElementRef, AfterViewInit, OnChanges, Renderer2, ViewChild } from '@angular/core';
import { CurrencyPipe, getCurrencySymbol } from '@angular/common';
import { NgControl, ControlValueAccessor } from '@angular/forms';
import { CustomCurrencyPipe } from '../pipes/custom-currency.pipe';
import { ModalDirective } from 'ngx-bootstrap/modal';
@Directive({
selector: '[appCurencyFormat]',
providers: [CustomCurrencyPipe]
})
export class CurrencyFormatDirective implements OnInit{
//@Input('appNumberFormat') params: any;
@Input() decimalNumber: number = 2;
@Input() symbol: string = "symbol";
//@Input() OnlyNumber: boolean;
local: string;
decimal: string;
currency: string;
element: any;
@ViewChild(ModalDirective) childModal: ModalDirective;
constructor(private elementRef: ElementRef, private ngControl: NgControl, private currencyPipe: CustomCurrencyPipe, private _renderer: Renderer2) {
this.element = this.elementRef.nativeElement;
}
@HostListener('keydown', ['$event']) onKeyDown(event) {
let e = <KeyboardEvent>event;
//190 in array for .
if ([46, 8, 9, 27, 13, 110].indexOf(e.keyCode) !== -1 ||
// Allow: Ctrl+A
(e.keyCode === 65 && (e.ctrlKey || e.metaKey)) ||
// Allow: Ctrl+C
(e.keyCode === 67 && (e.ctrlKey || e.metaKey)) ||
// Allow: Ctrl+V
(e.keyCode === 86 && (e.ctrlKey || e.metaKey)) ||
// Allow: Ctrl+X
(e.keyCode === 88 && (e.ctrlKey || e.metaKey)) ||
// Allow: home, end, left, right
(e.keyCode >= 35 && e.keyCode <= 39)) {
// let it happen, don't do anything
return;
}
// Ensure that it is a number and stop the keypress
if ((e.shiftKey || (e.keyCode < 48 || e.keyCode > 57)) && (e.keyCode < 96 || e.keyCode > 105)) {
e.preventDefault();
}
}
@HostListener('focus', ['$event.target.value'])
onFocus(value: any) {
this.ctrl.setValue(this.currencyPipe.convertToNumber(value));
}
@HostListener('blur', ['$event.target.value'])
onBlur(value: any) {
this.ctrl.setValue(this.currencyPipe.transform(value, this.decimalNumber, this.symbol));
}
get ctrl() {
return this.ngControl.control;
}
}
Мое решение - это что-то с заданным интервалом в ngOnInit...
ngOnInit() {
let m = window.setInterval(() => {
console.info("Upao sam");
console.info(this.ctrl.value);
if (this.ctrl.value) {
console.info(this.ctrl.value);
if (seted) {
window.clearInterval(m);
} else {
seted = true;
this.ctrl.setValue(this.currencyPipe.transform(this.ctrl.value, this.decimalNumber, this.symbol));
}
}
}, 500);
}
Кто-нибудь знает, какой HostListener я могу использовать для этого, чтобы попытаться избежать использования window.setInterval(). Или, если кто-нибудь знает, как я могу решить эту проблему?
ОБНОВИТЬ
ngOnChanges() возникает не каждый раз, поэтому выбранный повторяющийся вопрос не может решить мою проблему.
@jonrsharpe просто возможно ... ngOnChanges не поднимается каждый раз
В чем сложность использования уже имеющегося валютная труба
Вы не можете использовать его на входе. В первый раз вы введете, например, 500, и канал вернет 500 долларов, в следующий раз вам нужно будет удалить $, если вы хотите использовать канал, потому что канал ожидает число, а не строку
вы можете добавить по умолчанию 0 или ничего
я не понимаю, что ты имеешь в виду
Можете ли вы создать демонстрацию проблемы stackblitz/codesandbox?
Я могу попробовать @MunimMunna
Я получаю сообщение об ошибке: Ошибка в src/main.ts Недостаточно места в стеке в Stackblitz для Angular Project. Может быть, вы знаете, как я могу это решить





Вот что вы можете сделать, чтобы решить эту проблему:
<input [ngModel] = "item | customCurrencyPipe:'USD':'symbol':'2.2'" name = "inputField" type = "text"
(ngModelChange) = "item = $event||0.00" [ngModelOptions] = "{updateOn:'blur'}"/>
1) Разделите привязку на одностороннюю привязку и привязку события.
2) С привязкой к событию "ngModelChange" восстанавливает значение с предоставленным вводом.
3) Обновить значение на «размытие», поэтому любой текст, который не является числом, не может быть обслужен // необязательно в зависимости от требований
4) customCurrencyPipe : это будет иметь функции валютной трубы по умолчанию, но не будет увеличиваться, если номер не указан, вместо этого будет возвращено значение по умолчанию или не будут разрешены другие числа, кроме чисел // в зависимости от требования
Выполняя описанные выше шаги, вы сможете достичь желаемых результатов с меньшим количеством взломов и многообещающим решением.
Какое событие я могу использовать для updateOn, если я хочу форматировать числа при нажатии клавиши. Я пробовал с keydown, но это не работает. По какой-то причине я не получаю только форматированные числа $, и только одноразовый отладчик показывает мне, что это в моей функции преобразования customCurrencyPipe. В другой раз я не знаю, куда он идет.
вы можете использовать: [ngModelOptions] = "{ debounce: 200, updateOn: 'change' }"
Как я могу перемещать вертикальную линию... Я могу ввести 1-2 числа, но все остальные будут в десятичной части числа
Это вы должны указать в своем пользовательском канале, в котором вы передаете «2.2» в качестве второго параметра. Таким образом, основная логика заключалась бы в том, чтобы получить число и отфильтровать его с помощью канала, чтобы добавить к нему символ. Теперь время отката поможет вам дождаться, пока пользователь перестанет печатать.
хм, я могу использовать ваше решение, но я хочу форматировать числа при фокусировке и размытии. На фокусе я хотел бы использовать директиву, на размытии я хотел бы использовать трубу. Например, $123 123,00 — в фокусе будет 123123,00, когда пользователь изменит номер при размытии, он получит $ и разделитель тысяч. Является ли это возможным?
В идеале разделитель должен соответствовать региону, для этого вам нужно установить место действия , однако, если вы хотите, чтобы разделитель был только для тысяч, вы можете написать свое регулярное выражение в самом своем канале и передать его на вывод
да, у меня есть локаль, но как ее можно использовать при вводе?
Я бы использовал все средства доступа к значениям. Это то, что я использую для всех своих входов. Вы можете написать что-то вроде этого
@Component({
selector: 'ks-input',
template: `<input [(ngModel)] = "value" />`,
styleUrls: ['./whatever.component.css'],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => InputComponent),
multi: true
}
]
})
export class InputComponent implements ControlValueAccessor {
@Input('value') _value = '';
onChange: any = () => {
};
onTouched: any = () => {
};
get value() {
return this._value;
}
set value(val) {
this._value = val;
this.onChange(val);
this.onTouched();
}
registerOnChange(fn) {
this.onChange = fn;
}
registerOnTouched(fn) {
this.onTouched = fn;
}
writeValue(value) {
this.value = value;
}
}Когда вы передаете переменную этому компоненту, она сначала проходит через функцию writeValue, где вы можете выполнить любое начальное форматирование. вы также можете делать что угодно в функции установки значения, которая вызывается каждый раз, когда изменяется значение/ввод переменной ngModel.
Хм, хорошее решение, это может быть. Я обязательно попробую, если не найду лучшего решения.
@GlacialMan Да, все мои входные данные формы намного сложнее, чем очень простой пример, который я показал с такими вещами, как формат валюты, формат номера телефона и т. д. и я использую это, и это творит чудеса. С помощью angular я обнаружил, что это единственный способ получить полный контроль над элементами управления формой. Я думаю, что это было на самом деле разработано для вашего конкретного случая использования.
Я дам вам награду, потому что я пробовал все другие решения. Ваше решение должно работать, но оно намного сложнее.
ngDoCheck() вместо ngOnChanges().ngDoCheck() — это гарантированный хук жизненного цикла, который будет запускаться каждый раз при обнаружении изменения триггера Angular.
Небольшой рабочий пример показывает, что ngDoCheck() ловушка директивы всегда будет срабатывать при изменении.
Чтобы понять, почему ngOnChanges() не поднимается все время и зачем вместо этого использовать ngDoCheck():
Всякий раз, когда изменяется связывающее свойство Input из директивы, оно запускает обнаружение изменений Angular. И как Angular определить, изменился ли этот Input? Он сравнивает объект Простое изменение, представляющий, что старые и новые значения Input, если между ними есть различия, то Angular запускает крючок жизненного цикла ngOnChanges().
Таким образом, есть случай, когда вы что-то ввели, но сравниваемое значение не изменилось (например, вы изменили Input с помощью mutate и свойства Object, поэтому на самом деле старое и новое значение Простое изменение одинаковы, потому что Angular сравнивает ссылку, не значение), в этом случае он не поднимает хук ngOnChange().
Но ngDoChek() — это нечто другое. Я буду использовать эту информацию из очень хорошей статьи относительно ngDoCheck():
Предположим, у нас есть следующие компоненты/дерево директив:
Component A
Component B
Component C
Поэтому, когда Angular запускает обнаружение изменений, порядок операций следующий:
Checking A component:
- update B input bindings
- call NgDoCheck on the B component
- update DOM interpolations for component A
Checking B component:
- update C input bindings
- call NgDoCheck on the C component
- update DOM interpolations for component B
Checking C component:
- update DOM interpolations for component C
Как вы видите, всякий раз, когда выполняется обнаружение изменений, всегда вызывается ngDoCheck(). Кроме того, он также вызывается при инициализации компонента/директивы (сбросьте мой пример блицстека, чтобы увидеть это). Так:
Отформатируйте значение ввода внутри ngDoCheck(), чтобы решить вашу проблему.
Ошибка: ExpressionChangedAfterItHasBeenCheckedError: выражение изменилось после проверки. Предыдущее значение: «модель: не определено». Текущее значение: «модель: ноль».
и он показывает мне ошибку каждый раз, когда я ввожу номер по номеру...
У меня была точно такая же проблема, как у вас. Я смог решить эту проблему с помощью хука NgControl для доступа к элементу управления формой из директивы.
Я добавил NgControl в конструктор директив:
constructor(
private el: ElementRef,
private decimalPipe: DecimalPipe,
@Self() private ngControl: NgControl) { }
Таким образом, я смог подписаться на наблюдаемые изменения значения элемента управления формы. Наблюдаемые триггеры срабатывают как при изменении пользователя, так и при изменении кода, поэтому, чтобы избежать форматирования, пока пользователь печатает, я проверяю, что элемент управления нетронутый:
ngOnInit(): void {
this.ngControl.valueChanges
.subscribe(value => {
if (this.ngControl.pristine) {
this.el.nativeElement.value =
this.decimalPipe.transform(value, `1.2-2`);
}
});
Я надеюсь, что это может быть полезно для кого-то еще. Я почерпнул идею из этой статьи https://netbasal.com/attribute-directives-angular-forms-b40503643089
Возможный дубликат Как определить, когда значение @Input() изменяется в Angular?