Изменить значение FormControl из ControlValueAccessor

#angular #typescript #controlvalueaccessor

#angular #typescript #controlvalueaccessor

Вопрос:

Я пытаюсь создать директиву только для регистра. Я создал следующий код, но у меня возникла проблема при попытке создать FormGroup.value. Директива изменяет значение визуально (с помощью _Renderer2), но FormControl по-прежнему имеет исходное значение, и при получении данных из формы с помощью FormGroup.value значения указаны в нижнем регистре. Возможно ли это исправить? Спасибо

 <form [formGroup]="formGroup">
    <mat-form-field>
        <input placeholder="Street" formControlName="streetName" matInput uppercase>
    </mat-form-field>
</form>

import { Directive, ElementRef, Optional, Renderer2, Self } from '@angular/core';
import { ControlValueAccessor, NgControl } from '@angular/forms';

@Directive({
  selector: "textarea[uppercase], input[uppercase]",
  host: {
    '(input)': 'writeValue($event.target.value)',
    '(blur)': 'onTouched()',
  }
})
export class UppercaseDirective implements ControlValueAccessor {

  onChange = (_: any) => {
    console.log("onChange", _)
  };

  onTouched = () => {
    console.log("onTouched")
  };

  constructor(private _renderer: Renderer2, private _elementRef: ElementRef, @Optional() @Self() public ngControl: NgControl) {
    ngControl.valueAccessor = this;
  }

  writeValue(value: any): void {
    this._renderer.setProperty(this._elementRef.nativeElement, 'value', this.transformValue(value));
  }

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

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

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

  private transformValue(value: string): string {
    return typeof value === 'string'
      ? value?.toUpperCase()
      : value;
  }
}
  

Комментарии:

1. Это имеет смысл, вы меняете DOM так, что angular не распознает это. Я не уверен, как вам помочь, поскольку прошло около 9 месяцев с тех пор, как я в последний раз использовал angular, но я думаю, что вам нужно попытаться получить сам «элемент управления формой»

2. Директивы изменяют только то, что мы видим в DOM, они фактически не изменяют само значение.

Ответ №1:

Вы смешиваете понятия пользовательского элемента управления формой и директивы. Я не знаю, есть ли другой подход, но, если вы хотите создать директиву, которая преобразует один ввод, вы можете получить его, создав HTMLEvent . Некоторым нравится

 @Directive({
  selector: "[toUpperCase]"
})
export class ToUpperCaseDirective implements AfterViewInit {
  constructor(private elementRef: ElementRef) {}

  @HostListener("input", ["$event"]) onKeyDown(event: KeyboardEvent) {
    this.setUpper();
  }
  ngAfterViewInit() {
    setTimeout(() => {
      this.setUpper();
    });
  }
  setUpper() {
    const target = this.elementRef.nativeElement;
    let pos = target.selectionStart; //get the position of the cursor
    target.value = target.value.toUpperCase();
    var evt = document.createEvent("HTMLEvents");
    evt.initEvent("input", false, true);
    this.elementRef.nativeElement.dispatchEvent(evt);
    target.selectionStart = target.selectionEnd = pos; //return the position
  }
}
  

ПРИМЕЧАНИЕ: На самом деле я не рассматриваю директиву как хороший подход к проблеме ввода в верхнем регистре. Для меня лучше просто использовать css

 <input style="text-transform:uppercase">
  

Затем, когда мы хотим проверить значение, преобразуйте значение, используя верхний регистр.

Комментарии:

1. Спасибо за ответ. Делать это с помощью CSS для меня бесполезно, потому что это просто визуальное изменение, когда я получаю данные из formgroup.value, они в нижнем регистре. Я проверю вашу директиву