Формирование числа/валюты в директиве Angular 6

Я создал валютную директиву, которую я буду использовать в каждом элементе ввода, которая нуждается в валютном формате.

Итак, у меня есть 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() возникает не каждый раз, поэтому выбранный повторяющийся вопрос не может решить мою проблему.

Возможный дубликат Как определить, когда значение @Input() изменяется в Angular?

jonrsharpe 10.07.2019 08:50

@jonrsharpe просто возможно ... ngOnChanges не поднимается каждый раз

GlacialMan 10.07.2019 08:54

В чем сложность использования уже имеющегося валютная труба

yanky_cranky 16.07.2019 14:28

Вы не можете использовать его на входе. В первый раз вы введете, например, 500, и канал вернет 500 долларов, в следующий раз вам нужно будет удалить $, если вы хотите использовать канал, потому что канал ожидает число, а не строку

GlacialMan 16.07.2019 14:29

вы можете добавить по умолчанию 0 или ничего

yanky_cranky 16.07.2019 14:32

я не понимаю, что ты имеешь в виду

GlacialMan 16.07.2019 14:34

Можете ли вы создать демонстрацию проблемы stackblitz/codesandbox?

Munim Munna 16.07.2019 16:14

Я могу попробовать @MunimMunna

GlacialMan 17.07.2019 09:31

Я получаю сообщение об ошибке: Ошибка в src/main.ts Недостаточно места в стеке в Stackblitz для Angular Project. Может быть, вы знаете, как я могу это решить

GlacialMan 17.07.2019 09:55
stackblitz.com/edit/angular-hlkm6y вот это, но я не уверен, что это работает, я не могу попробовать на stackblitz
GlacialMan 18.07.2019 12:56
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Angular и React для вашего проекта веб-разработки?
Angular и React для вашего проекта веб-разработки?
Когда дело доходит до веб-разработки, выбор правильного front-end фреймворка имеет решающее значение. Angular и React - два самых популярных...
Эпизод 23/17: Twitter Space о будущем Angular, Tiny Conf
Эпизод 23/17: Twitter Space о будущем Angular, Tiny Conf
Мы провели Twitter Space, обсудив несколько проблем, связанных с последними дополнениями в Angular. Также прошла Angular Tiny Conf с 25 докладами.
Угловой продивер
Угловой продивер
Оригинал этой статьи на турецком языке. ChatGPT используется только для перевода на английский язык.
Мое недавнее углубление в Angular
Мое недавнее углубление в Angular
Недавно я провел некоторое время, изучая фреймворк Angular, и я хотел поделиться своим опытом со всеми вами. Как человек, который любит глубоко...
Освоение Observables и Subjects в Rxjs:
Освоение Observables и Subjects в Rxjs:
Давайте начнем с основ и постепенно перейдем к более продвинутым концепциям в RxJS в Angular
2
10
9 114
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Вот что вы можете сделать, чтобы решить эту проблему:

<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. В другой раз я не знаю, куда он идет.

GlacialMan 17.07.2019 10:11

вы можете использовать: [ngModelOptions] = "{ debounce: 200, updateOn: 'change' }"

yanky_cranky 17.07.2019 11:24

Как я могу перемещать вертикальную линию... Я могу ввести 1-2 числа, но все остальные будут в десятичной части числа

GlacialMan 17.07.2019 13:53

Это вы должны указать в своем пользовательском канале, в котором вы передаете «2.2» в качестве второго параметра. Таким образом, основная логика заключалась бы в том, чтобы получить число и отфильтровать его с помощью канала, чтобы добавить к нему символ. Теперь время отката поможет вам дождаться, пока пользователь перестанет печатать.

yanky_cranky 17.07.2019 14:22

хм, я могу использовать ваше решение, но я хочу форматировать числа при фокусировке и размытии. На фокусе я хотел бы использовать директиву, на размытии я хотел бы использовать трубу. Например, $123 123,00 — в фокусе будет 123123,00, когда пользователь изменит номер при размытии, он получит $ и разделитель тысяч. Является ли это возможным?

GlacialMan 22.07.2019 09:17

В идеале разделитель должен соответствовать региону, для этого вам нужно установить место действия , однако, если вы хотите, чтобы разделитель был только для тысяч, вы можете написать свое регулярное выражение в самом своем канале и передать его на вывод

yanky_cranky 22.07.2019 09:45

да, у меня есть локаль, но как ее можно использовать при вводе?

GlacialMan 22.07.2019 15:12
Ответ принят как подходящий

Я бы использовал все средства доступа к значениям. Это то, что я использую для всех своих входов. Вы можете написать что-то вроде этого

@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 17.07.2019 10:47

@GlacialMan Да, все мои входные данные формы намного сложнее, чем очень простой пример, который я показал с такими вещами, как формат валюты, формат номера телефона и т. д. и я использую это, и это творит чудеса. С помощью angular я обнаружил, что это единственный способ получить полный контроль над элементами управления формой. Я думаю, что это было на самом деле разработано для вашего конкретного случая использования.

Matt 17.07.2019 10:51

Я дам вам награду, потому что я пробовал все другие решения. Ваше решение должно работать, но оно намного сложнее.

GlacialMan 22.07.2019 15:47
stackoverflow.com/questions/51474527/…
GlacialMan 22.07.2019 15:48

Решение заключается в форматировании значения ввода внутри 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: выражение изменилось после проверки. Предыдущее значение: «модель: не определено». Текущее значение: «модель: ноль».

GlacialMan 22.07.2019 15:11

и он показывает мне ошибку каждый раз, когда я ввожу номер по номеру...

GlacialMan 22.07.2019 15:44

У меня была точно такая же проблема, как у вас. Я смог решить эту проблему с помощью хука 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

Другие вопросы по теме