Angular 9 中用于修剪空格的自定义指令

Custom directive for trimming whitespace in Angular 9

我创建了一个自定义指令来从输入框中删除空格,它在 Angular 版本 7 之前完全正常工作,但在 Angular 版本 9 中不起作用。

import {
    Directive,
    ElementRef,
    Output,
    EventEmitter,
} from '@angular/core';
@Directive({
    selector: '[trim]',
    host: {
        '(blur)': 'onBlur()'
    }
})
export class TrimDirective {
    @Output() ngModelChange: EventEmitter < any > = new EventEmitter();
    constructor(private element: ElementRef) {}
    onBlur() {
        (this.element.nativeElement as HTMLInputElement).value = (this.element.nativeElement as HTMLInputElement).value.trim();
        this.ngModelChange.emit((this.element.nativeElement as HTMLInputElement).value.trim());
    }
}

在模糊事件中它应该trim 空格并更新 ngModel,但它没有更新 ngModel

为了更新输入值,他们同时使用 setProperty() 和 setAttribute()

import { Directive, EventEmitter, Input, ChangeDetectorRef, Output, ElementRef, HostListener, Inject, Renderer2 } from '@angular/core';
import { NgModel } from '@angular/forms';

@Directive({
  selector: '[appTrim]'
})
export class TrimDirective {
  constructor(
    private renderer: Renderer2,
    private elementRef: ElementRef,
    private ngModel: NgModel) { }

  @HostListener("blur")
  onBlur() {
    let value = this.ngModel.model;

    if(value) {
      value = value.trim();
      this.renderer.setProperty(
        this.elementRef.nativeElement, "value", value);
      this.renderer.setAttribute(
        this.elementRef.nativeElement, "value", value);
      this.ngModel.update.emit(value);
    } else {
      this.renderer.setProperty(
        this.elementRef.nativeElement, "value", null);
      this.renderer.setAttribute(
        this.elementRef.nativeElement, "value", null);
      this.ngModel.update.emit("");
    }
  }
}

示例link

以下是我们的实现方式,从 Angular 4.x 到 11.x

如您所见,我们正在使用选择器,以便将指令应用于所有输入/文本区域元素,而无需显式使用该指令。

这可以很容易地改变,而是使用“app-trim”

import { Directive, HostListener, forwardRef } from "@angular/core";
import { DefaultValueAccessor, NG_VALUE_ACCESSOR } from "@angular/forms";

const TRIM_VALUE_ACCESSOR: any = {
  provide: NG_VALUE_ACCESSOR,
  useExisting: forwardRef(() => TrimValueDirective),
  multi: true
};

/**
 * The trim accessor for writing trimmed value and listening to changes that is
 * used by the {@link NgModel}, {@link FormControlDirective}, and
 * {@link FormControlName} directives.
 */
@Directive({
  selector: `
    input:not([type=checkbox]):not([type=radio]):not([type=password]):not([readonly]):not(.app-trim-ignore)[formControlName],
    input:not([type=checkbox]):not([type=radio]):not([type=password]):not([readonly]):not(.app-trim-ignore)[formControl],
    input:not([type=checkbox]):not([type=radio]):not([type=password]):not([readonly]):not(.app-trim-ignore)[ngModel],
    textarea:not([readonly]):not(.app-trim-ignore)[formControlName],
    textarea:not([readonly]):not(.app-trim-ignore)[formControl],
    textarea:not([readonly]):not(.app-trim-ignore)[ngModel],
    :not([readonly]):not(.app-trim-ignore)[ngDefaultControl],
    [app-trim]
  `,
  providers: [TRIM_VALUE_ACCESSOR]
})
export class TrimValueDirective extends DefaultValueAccessor {

  @HostListener("input", ["$event.target.value"])
  ngOnChange = (val: string): void => {
    this.onChange(val.trim());
  };

  @HostListener("blur", ["$event.target.value"])
  applyTrim(val: string): void {
    this.writeValue(val.trim());
  }

  writeValue(value: any): void {
    if (typeof value === "string") {
      value = value.trim();
    }

    super.writeValue(value);
  }

}