4

我们在网页上有一个输入字段,必须在用户输入该数据的同时进行修剪。由于输入绑定到 Angular 表单,因此表单中的值也必须被修剪。我使用 Angular 7

import {
  Directive,
  ElementRef,
  forwardRef,
  HostListener,
  Input,
  Renderer2
} from "@angular/core";
import {
  ControlValueAccessor,
  NG_VALUE_ACCESSOR
} from "@angular/forms";


@Directive({
  selector: "[ebppInputTextTrimmer]",
  providers: [{
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(() => InputTextTrimmerDirective),
    multi: true
  }]
})
export class InputTextTrimmerDirective implements ControlValueAccessor {
  @Input() prevVal: string;

  @Input() isTrimEnabled: boolean;

  onChange = (_: any) => {
  }

  onTouched = () => {
  }

  constructor(
      private _renderer: Renderer2,
      private _elementRef: ElementRef) {
  }

  writeValue(value: any): void {
    const normalizedValue = value == null ? "" : value;
    this._renderer.setProperty(this._elementRef.nativeElement, "value", normalizedValue);
  }

  registerOnChange(fn: (_: any) => void): void {
    this.onChange = fn;
  }

  registerOnTouched(fn: () => void): void {
    this.onTouched = fn;
  }

  setDisabledState(isDisabled: boolean): void {
    this._renderer.setProperty(this._elementRef.nativeElement, "disabled", isDisabled);
  }

  @HostListener("input", ["$event.target.value"])
  handleInput(inputValue: any): void {
    let valueToProcess = inputValue;
    if (this.isTrimEnabled) {
      valueToProcess = inputValue.trim();
    }

    this.onChange(valueToProcess);
    // set the value that is trimmed in the view
    this._renderer.setProperty(this._elementRef.nativeElement, "value", valueToProcess);
  }

}

显示的代码对我来说很好。我想知道是否有更简单的解决方案。

4

5 回答 5

3

您可以创建一个自定义值访问器,如下所示作为指令:

const TRIM_VAL_ACCESSOR = new Provider(
  NG_VALUE_ACCESSOR, {useExisting: forwardRef(() => TrimValAccessor), multi: true});

@Directive({
  selector: 'input[trimval]',
  host: { '(keyup)': 'valOnChange($event.target)' },
  providers: [ TRIM_VAL_ACCESSOR ]
})
export class TrimValAccessor extends DefaultValueAccessor {
  onChange = (_) => {};
  onTouched = () => {};

  constructor(private renderer:Renderer) {
  }

  writeValue(value:any):void {
    if (value!=null) {
      super.writeValue(value.toString().trim());
    }
  }

  valOnChange(el) {
    let val = el.value.trim();
    this.renderer.setElementProperty(el, 'value', val);
    this.onChange(val);
  }
}

在模块中提供参考:

declarations: [ TrimValAccessor ]

或在这样的组件中

@Component({
  (...)
  template: `
    <input type="text" trimval/>
  `,
  directives: [ TrimValAccessor ]
})

在输入标签中使用来修剪值

<input type="text" trimval/>
于 2019-04-11T07:22:02.653 回答
2

您可以将实现简化为

@Directive({
  selector: "[ebppInputTextTrimmer]"
})

export class InputTextTrimmerDirective {
  @Input("ebppInputTextTrimmer") isTrimEnabled = false;
  @Output() ngModelChange = new EventEmitter();

  constructor(
      private _renderer: Renderer2,
      private _elementRef: ElementRef) {
  }

  @HostListener("input", ["$event.target.value"])
  handleInput(inputValue: any): void {
    if (this.isTrimEnabled) {
      const valueToProcess = inputValue.trim();
      this._renderer.setProperty(this._elementRef.nativeElement, "value", valueToProcess);
      this.ngModelChange.emit(valueToProcess);
    }
  }

}

并通过使用有条件地将其添加到模板中

<input
[(ngModel)]="filter[columnConfig.key]"
...
[ebppInputTextTrimmer]="isAutoTrim(columnConfig)"
...
>
于 2019-04-11T12:14:19.407 回答
1

如果您使用Reactive Forms,您可以valueChanges订阅值,然后手动修剪并再次修补值。

一个完整的例子是:


export class SomeComponent implements OnInit, OnDestroy {


  trimSubscription: Subscription;

  ngOnInit(): void {


    this.trimSubscription = this.materialForm.valueChanges
      .pipe(
        debounceTime(1000),
      )
      .subscribe((formValues) => {

        let trimmedValues = Object
          .keys(formValues)
          .reduce((previous, currentKey) => ({
            ...previous,
            [currentKey]: typeof formValues[currentKey] == 'string' ? formValues[currentKey].trim() : formValues[currentKey],
          })
            , {});

        this.materialForm.patchValue(trimmedValues);
      });
  }

  ngOnDestroy(){
    this.trimSubscription.unsubscribe();
  }

  materialForm = new FormGroup({
    desc: new FormControl(''),
    descAr: new FormControl(''),
    name: new FormControl('', [Validators.required]),
    nameAr: new FormControl('', [Validators.required]),
    price: new FormControl('', [Validators.required, Validators.min(0)]),
    categoryId: new FormControl('', [Validators.required]),
  });

  // ...
}

于 2021-02-27T05:59:17.490 回答
1

我尝试通过在父表单中收听control.valueChnagesobservable 并在那里修剪和设置值来解决这个问题。

在您的控件值访问器类中有一个方法,将其注册为 onChange 方法。

onChange () {};

registerOnChange(fn) {
  this.onChange = fn
}

让你的输入框像:

<input type="text" [value]="value" (input)="onChange($event.target.value)">

在您定义表单的父组件中。

ngOnInit() {
  this.parentForm = new FormGroup({
    name: new FormControl(''),
    age: new FormControl(12)
  })

  this.valueChangesSub = this.parentForm.get('age').valueChanges.pipe(
    debounceTime(50),
    switchMap(newVal => of(newVal))
  ).subscribe((newVal) => {
    this.parentForm.get('age').setValue(newVal, {emitEvent: false});
    this._cdr.detectChanges();
    this.parentForm.get('age').setValue(newVal.trim(), {emitEvent: false});
  })
}

假设,age是控件值访问器类的表单控件。你会注意到我设置了两次值,这是为了让角度与变化检测一起工作,并在修剪完成时更新你的输入视图。

https://stackblitz.com/edit/angular-ulyju7?file=src%2Fapp%2Fapp.component.ts

于 2019-04-11T07:34:05.207 回答
0

这不是纯粹的 Angular 方式,但有时它看起来更有效。在提交事件上调用它。它使用 lo-dash 来修剪(IE 还在这里)。

trimInputs() {
    const cssSelector = 'input[type="text"]:not(.no-trim), textarea:not(.no-trim)';
    const inputs: (HTMLInputElement | HTMLTextAreaElement)[] = this.el.nativeElement.querySelectorAll(cssSelector) || [];
    inputs.forEach((input) => {
        if (_.isString(input.value)) {
            input.value = _.trim(input.value);
            input.dispatchEvent(new Event('input'));
        }
    })
}
于 2021-07-03T22:39:57.767 回答