Angular 4 监听 setValue 或 patchValue 而不是使用 valueChanges

Posted

技术标签:

【中文标题】Angular 4 监听 setValue 或 patchValue 而不是使用 valueChanges【英文标题】:Angular 4 Listen for setValue or patchValue instead of using valueChanges 【发布时间】:2017-10-26 15:44:59 【问题描述】:

我以反应形式向字段添加了一个浮点指令,该指令将逗号添加到每 1000 个字段中,并将.00 附加到字段值,专门用于 UI 中的可读性,效果很好。

onBlur 添加格式 onFocus 删除格式

当使用现有值加载表单时,我希望对这些值进行格式化,因此我将其添加到浮点指令中,以便在使用setValuepatchValue 填充表单字段时对值进行一次格式化,效果很好。

浮点指令的片段

public ngOnInit() 
  this.formatFloat();


private formatFloat() 
  const handle = this.ngControl.valueChanges
    .subscribe((value: string) => 
      const float = this.getFloat();
      if (float) 
        this.element.value = this.format(value);
      
      handle.unsubscribe();
    );

** 在下面添加了完整的指令,但这只是真正重要的部分。

但是,如果您在填写空表单时将表单字段动态添加到 FormArray,则不会触发一次性格式化,因此您在字段中键入的第一个数字会添加格式。例如,打开一个空表单,单击一个按钮添加一个动态字段,在字段中输入1 会触发一次valueChange,输入现在有1.00,用户将继续输入1.001244而不是11244

我知道patchValuesetValue通过emitEventdocs直接链接到valueChanges,但是有没有办法监听setValuepatchValue变化而不是监听@987654337 @?或者是否有另一种方法可以让它工作,但仍然具有现有功能,因为即使只是听 setValuepatchValue 也意味着一次性格式订阅仍然有效。

浮点指令

import  Directive, HostListener, ElementRef, OnInit  from '@angular/core';
import  DecimalPipe  from '@angular/common';
import  FormGroup, NgControl  from '@angular/forms';

@Directive(
  selector: '[cfFloat]',
  providers: [DecimalPipe] // TODO: why do I need this?
)
export class FloatDirective implements OnInit 
  public element: htmlInputElement;

  constructor(
    private elementRef: ElementRef,
    private decimalPipe: DecimalPipe,
    private ngControl: NgControl
  ) 
    this.element = this.elementRef.nativeElement;
  

  @HostListener('blur', ['$event'])
  onBlur(event: KeyboardEvent) 
    const float = this.getFloat();
    if (float) 
      this.element.value = this.format(float);
    
  

  @HostListener('focus', ['$event'])
  onFocus(event: KeyboardEvent) 
    const float = this.getFloat();
    if (float) 
      this.element.value = this.replace(float);
    
  

  public ngOnInit() 
    this.formatFloat();
  

  private formatFloat() 
    const handle = this.ngControl.valueChanges
      .subscribe((value: string) => 
        const float = this.getFloat();
        if (float) 
          this.element.value = this.format(value);
        
        handle.unsubscribe();
      );
  

  private getFloat(): string 
    const value = this.element.value;
    const float = this.replace(value);
    // Only perform an action when a floating point value exists and there are
    // no errors, otherwise leave the erroneous value to be fixed manually by
    // ignoring an action
    if (value && float && this.checkIsValid()) 
      return float;
    
  

  private checkIsValid(): boolean 
    return !this.ngControl.control.errors;
  

  private replace(value: string): string 
    return value.replace(/[^\d\.]+/g, '');
  

  private format(value: string) 
    return this.decimalPipe.transform(value, '1.2-2');
  


【问题讨论】:

【参考方案1】:

好的,想通了。不是一个可怕的修复,但似乎它可能会更优雅......总是提出建议。

private onInitFormatHandler: Subscription; // <-- ADDED HANDLER AS MEMBER VARIABLE INSTEAD

@HostListener('focus', ['$event'])
onFocus(event: KeyboardEvent) 

  // Remove initial formatting subscription since no patch of the value has
  // occurred, and is no longer likely to occur if the user is actively
  // applying focus
  // ---
  // NOTE: Not unsubscribing causes formatting to occur on dynamically added
  // fields on the first change of the input value prior to blur
  if (!this.onInitFormatHandler.closed)  // <-- ADDED CHECK AND EXTRA UNSUBSCRIPTION
    this.onInitFormatHandler.unsubscribe();
  

  const float = this.getFloat();
  if (float) 
    this.element.value = this.replace(float);
  


public ngOnInit() 
  this.formatFloat();


/**
 * Format the input value only once after the initial form response has
 * patched the model.
 * ---
 * NOTE: Format handler is stored and unsubscribed either on valueChange, or
 * if focus is applied to the field, whichever occurs first.
 */
private formatFloat() 
  this.onInitFormatHandler = this.ngControl.valueChanges // <-- UPDATED HANDLER TO BE MEMBER VARIABLE
    .subscribe((value: string) => 
      const float = this.getFloat();
      if (float) 
        this.element.value = this.format(value);
      
      this.onInitFormatHandler.unsubscribe(); // <-- UPDATED HANDLER TO BE MEMBER VARIABLE
    );

【讨论】:

以上是关于Angular 4 监听 setValue 或 patchValue 而不是使用 valueChanges的主要内容,如果未能解决你的问题,请参考以下文章

Angular2 指令监听父组件回调或更改

带有来自服务的数据的 Angular FormArray setValue

使用野狗(Wilddog)云setValue写入数据

【VUE】监听页面返回或移动端物理返回按钮键

Angular.JS中使用$watch监听模型变化

Angular.js中使用$watch监听模型变化