Угловой 7 - ControlValueAccessor - обрезать входные значения, которые связаны с формой - PullRequest
1 голос
/ 11 апреля 2019

У нас есть поле ввода на веб-странице, которое должно быть обрезано в то время, когда пользователь вводит эти данные. Поскольку входные данные привязаны к угловой форме, значение в форме также должно быть обрезано. Я использую 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);
  }

}

Показанный код отлично работает для меня. Интересно, есть ли более простое решение?

Ответы [ 3 ]

1 голос
/ 11 апреля 2019

Я попытался решить эту проблему, прослушав control.valueChnages наблюдаемый в родительской форме и обрезав и установив значение там.

Имейте метод в своем классе доступа к значению элемента управления, зарегистрируйте его как метод 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

1 голос
/ 11 апреля 2019

Вы можете упростить реализацию до

@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)"
...
>
1 голос
/ 11 апреля 2019

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

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/>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...