【问题标题】:Formating Number / Currency in Angular 6 Directive在 Angular 6 指令中格式化数字/货币
【发布时间】:2019-11-19 17:38:42
【问题描述】:

我创建了一个货币指令,我将在每个需要货币格式的输入元素中使用它

所以我有 2 个 Host Listeners,一个是 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.log("Upao sam");
        console.log(this.ctrl.value);
        if (this.ctrl.value) {
          console.log(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 不会每次都提高
  • 使用已经可用的currency-pipe有什么挑战
  • 您不能在输入中使用它。第一次您将输入例如 500,管道将返回 $500,下次如果您想使用管道,则需要删除 $,因为管道期望数字而不是字符串
  • 你能创建一个关于这个问题的 stackblitz/codesandbox 演示吗?

标签: angular format currency angular-directive


【解决方案1】:

我会使用整个值访问器。这就是我用于所有输入的内容。你可以写这样的东西

@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 是的,我所有的表单输入都比我展示的货币格式、电话号码格式等非常简单的示例复杂得多。我使用它,它创造了奇迹。有了角度,我发现这是完全控制表单控件的唯一方法。我认为这实际上是为您的特定用例设计的。
  • 我会给你一个赏金,因为我尝试了所有其他解决方案。您的解决方案必须有效,但要复杂得多。
【解决方案2】:

您可以采取以下措施来解决此问题:

<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 : 这将具有货币管道的默认功能,但如果未提供数字则不会升级,而是将返回默认值或不允许除数字以外 //根据需求

通过上述步骤,您将能够以更少的 hack 和有前途的解决方案实现预期的结果。

【讨论】:

  • 如果我想在 keyPressed 上格式化数字,我可以使用哪个事件进行 updateOn。我尝试使用 keydown 但它不起作用。出于某种原因,我没有得到 $ 仅格式化的数字,并且只有一次性调试器向我显示它在我的 customCurrencyPipe 函数转换中。还有一次我不知道它去了哪里。
  • 你可以使用:[ngModelOptions]="{ debounce:200,updateOn: 'change' }"
  • 如何移动垂直线...我可以输入 1-2 个数字,但所有其他数字都输入数字的小数部分
  • 您必须在自定义管道中解决此问题,其中您将“2.2”作为第二个参数传递。因此,基本逻辑是获取数字并使用管道对其进行过滤,以便在其前面加上符号。现在,去抖动时间可以帮助您等到用户停止输入
  • 嗯,我可以使用您的解决方案,但我想在焦点和模糊上格式化数字。在焦点上,我想使用指令,在模糊上我想使用管道。例如 $123,123.00 - 焦点将是 123123.00,当用户更改模糊上的数字时,它将获得 $ 和 , 千位分隔符。有可能吗?
【解决方案3】:

解决方案是格式化 ngDoCheck() 内的输入值,而不是 ngOnChanges()

ngDoCheck() 是一个有保证的生命周期钩子,每次检测到 Angular 触发器更改时都会运行。

A small working example表示指令的ngDoCheck()钩子总是会在有变化时触发。


为了理解为什么 ngOnChanges() 不会一直引发以及为什么使用 ngDoCheck() 来代替:

只要指令中的绑定 Input 属性发生更改,它就会触发 Angular 更改检测。以及 Angular 如何检测 Input 是否已更改?它比较Simple Change对象代表Input的新旧值,如果它们之间存在差异,则Angular触发ngOnChanges()生命周期钩子。

因此,您确实输入了某些内容,但比较的值没有改变(例如,您通过 mutate 和 Object 的属性更改了 Input ,所以实际上 Simple Change 的新旧值是相同的,因为 Angular 比较的是引用,而不是值),在这种情况下,它不会引发 ngOnChange() 钩子。 p>


但是 ngDoChek() 是不同的。关于ngDoCheck(),我将使用from a very good article 的这些信息:

假设我们有以下组件/指令树:

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()。除此之外,它还会在组件/指令初始化时调用(重置my blitzstack example 以查看)。所以:

格式化ngDoCheck()内的输入值会解决你的问题

【讨论】:

  • 错误:ExpressionChangedAfterItHasBeenCheckedError:表达式在检查后已更改。以前的值:'模型:未定义'。当前值:'模型:null'。
  • 每次按数字输入数字时都会显示错误...
【解决方案4】:

我遇到了和你一样的问题。我能够使用 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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-14
    • 1970-01-01
    • 1970-01-01
    • 2016-10-01
    • 2017-03-23
    相关资源
    最近更新 更多