programing

Angular 5 반응형 폼 입력의 파이프 사용 방법

yellowcard 2023. 8. 28. 20:57
반응형

Angular 5 반응형 폼 입력의 파이프 사용 방법

저는 입력이 통화 형식으로 강제될 수 있도록 반응형 형태로 파이프를 사용하는 방법을 찾고 있습니다.나는 이미 코드의 다른 영역에서 테스트한 이것을 위한 나만의 파이프를 만들어 놓았기 때문에 그것이 간단한 파이프로 작동한다는 것을 알고 있습니다.내 파이프 이름은 'udpCurrency'입니다.

스택 오버플로에서 찾을 수 있는 가장 가까운 대답은 다음과 같습니다. Angular2-View의 INPUT 요소에서 파이프 내의 파이프 사용입니다. 하지만 이것은 제 경우에는 작동하지 않고 제 폼이 반응적이라는 사실과 관련이 있다고 생각합니다.

다음은 모든 관련 코드입니다.

템플릿

<form [formGroup]="myForm" #f="ngForm">
  <input class="form-control col-md-6" 
    formControlName="amount" 
    [ngModel]="f.value.amount | udpCurrency" 
    (ngModelChange)="f.value.amount=$event" 
    placeholder="Amount">
</form>

구성 요소

import { Component, OnInit, HostListener } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';

export class MyComponent implements OnInit {
  myForm: FormGroup;

  constructor(
    private builder: FormBuilder
  ) {
    this.myForm = builder.group({
      amount: ['', Validators.required]
    });
  }    
}

오류:

ERROR Error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'undefined: '. Current value: 'undefined: undefined'

이는 템플릿 기반 폼과 반응형 폼을 혼합할 때 발생할 수 있습니다.당신은 서로 싸우는 두 개의 구속력을 가지고 있습니다.템플릿 기반 또는 반응형을 선택합니다.반응 경로로 가고 싶다면, 당신은 다음을 사용할 수 있습니다.[value]당신의 파이프를 위해...

이 파이프는 오히려 뷰에서 원하는 출력을 표시하기 위한 것입니다.

<form [formGroup]="myForm">
  <input 
    [value]="myForm.get('amount').value | udpCurrency"
    formControlName="amount" 
    placeholder="Amount">
</form>

저는 제가 이 일을 할 수 있다고 생각했지만, 알고 보니 제가 틀렸습니다(오답을 받아들였습니다).저는 단지 저에게 더 잘 작동하고 위의 댓글에서 제이콥 로버츠의 우려에 답하는 새로운 방식으로 제 논리를 다시 만들었습니다.다음은 저의 새로운 솔루션입니다.

템플릿:

<form [formGroup]="myForm">
  <input formControlName="amount" placeholder="Amount">
</form>

구성 요소:

import { Component, OnInit, HostListener } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { UdpCurrencyMaskPipe } from '../../../_helpers/udp-currency-mask.pipe';

export class MyComponent implements OnInit {
  myForm: FormGroup;

  constructor(
    private builder: FormBuilder,
    private currencyMask: UdpCurrencyMaskPipe,
  ) {
    this.myForm = builder.group({
      amount: ['', Validators.required]
    });

    this.myForm.valueChanges.subscribe(val => {
      if (typeof val.amount === 'string') {
        const maskedVal = this.currencyMask.transform(val.amount);
        if (val.amount !== maskedVal) {
          this.myForm.patchValue({amount: maskedVal});
        }
      }
    });
  }    
}

파이프:

import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
    name: 'udpCurrencyMask'
})
export class UdpCurrencyMaskPipe implements PipeTransform {
    amount: any;

    transform(value: any, args?: any): any {

        let amount = String(value);

        const beforePoint = amount.split('.')[0];
        let integers = '';
        if (typeof beforePoint !== 'undefined') {
            integers = beforePoint.replace(/\D+/g, '');
        }
        const afterPoint = amount.split('.')[1];
        let decimals = '';
        if (typeof afterPoint !== 'undefined') {
            decimals = afterPoint.replace(/\D+/g, '');
        }
        if (decimals.length > 2) {
            decimals = decimals.slice(0, 2);
        }
        amount = integers;
        if (typeof afterPoint === 'string') {
            amount += '.';
        }
        if (decimals.length > 0) {
            amount += decimals;
        }

        return amount;
    }
}

이제 여기서 제가 배운 것들이 몇 가지 있습니다.하나는 야곱이 말한 것이 사실이라는 것이었고, 다른 하나는 처음에는 효과가 있었을 뿐 가치가 바뀌었을 때 업데이트되지 않는다는 것입니다.또 다른 매우 중요한 점은 마스크를 위한 파이프가 뷰 파이프와 비교하여 완전히 다른 유형의 파이프가 필요하다는 것입니다.예를 들어 뷰의 파이프에서 이 값 "100"을 사용하여 "100.00"으로 변환할 수 있지만 값을 입력할 때 변환을 수행하지 않고 입력을 마친 후에만 변환을 수행하기를 원할 수 있습니다.이러한 이유로 저는 단순히 숫자가 아닌 숫자를 제거하고 소수점을 두 자리로 제한하는 통화 마스크 파이프를 만들었습니다.

사용자 지정 컨트롤을 작성하려고 했지만 FormControl 클래스에서 "onChange"를 재정의하는 것을 발견했습니다.ngModelChange더 쉬웠어요.emitViewToModelChange: false변경 이벤트의 반복 루프를 방지하기 위해 업데이트 로직 중에 중요합니다.통화에 대한 모든 파이핑은 구성 요소에서 발생하며 콘솔 오류가 발생할 염려가 없습니다.

<input matInput placeholder="Amount" 
  (ngModelChange)="onChange($event)" formControlName="amount" />
@Component({
  providers: [CurrencyPipe]
})
export class MyComponent {
  form = new FormGroup({
    amount: new FormControl('', { validators: Validators.required, updateOn: 'blur' })
  });

  constructor(private currPipe:CurrencyPipe) {}

  onChange(value:string) {
    const ctrl = this.form.get('amount') as FormControl;

    if(isNaN(<any>value.charAt(0))) {
      const val = coerceNumberProperty(value.slice(1, value.length));
      ctrl.setValue(this.currPipe.transform(val), { emitEvent: false, emitViewToModelChange: false });
    } else {
      ctrl.setValue(this.currPipe.transform(value), { emitEvent: false, emitViewToModelChange: false });
    }
  }

  onSubmit() {
    const rawValue = this.form.get('amount').value;

    // if you need to strip the '$' from your input's value when you save data
    const value = rawValue.slice(1, rawValue.length);

    // do whatever you need to with your value
  }
}

여기 있는 다른 답들은 저에게 제대로 먹히지 않았지만 저는 아주 잘 먹히는 방법을 찾았습니다.반응형 형식 값 Changes 서브스크립션 내에 파이프 변환을 적용해야 하지만 이벤트가 반복 루프를 만들지 않도록 이벤트를 내보내지 마십시오.

this.formGroup.valueChanges.subscribe(form => {
  if (form.amount) {
    this.formGroup.patchValue({
      amount: this.currencyMask.transform(form.amount)
    }, {
      emitEvent: false
    });
  }
});

이를 위해서는 파이프가 있었던 모든 것을 "비형식화"해야 합니다. 이는 일반적으로 파이프의 변환 함수 안에 있는 것과 마찬가지로 단순합니다.

value = value.replace(/\$+/g, '');

파이프 코드를 모르면 폼을 구성하는 위치 때문에 오류가 발생할 수 있습니다.

입력이 해결된 후 Angular의 변경 감지 후크를 사용하여 해당 값을 설정해 보십시오.

export class MyComponent implements OnInit {
  myForm: FormGroup;

  constructor(private builder: FormBuilder) { }    

  ngOnInit() {
    this.myForm = builder.group({
      amount: ['', Validators.required]
    });
  }

}

파이프를 지시문으로 감쌀 수 있습니다.예:

import { Directive, Input, OnInit } from '@angular/core';
import { NgControl } from '@angular/forms';

// The selector can restrict usage to a specific input.
// For example: 'input[type="number"][yourPipe]' for number type input.
@Directive({ selector: 'input[yourPipeSelector]' })
export class FormControlYourPipeDirective implements OnInit {
  /** Your optional pipe args. Same name as directive to directly pass the value. */
  @Input() public yourPipeSelector: unknown;
  
  /** Control event options. For example avoid unintentional change events. */
  private eventOptions = {
    onlySelf: true,
    emitEvent: false,
    emitViewToModelChange: false,
  };

  constructor(private ngControl: NgControl, private yourPipe: YourPipe) {}

  public ngOnInit(): void {
    this.ngControl.control.valueChanges.subscribe((value) => {
      this.ngControl.control.setValue(
        this.yourPipe.transform(value, this.yourPipeSelector),
        this.eventOptions
      );
    });
  }
}

파이프 모듈의 공급자에 파이프를 추가하는 것을 잊지 마십시오.파이프 모듈을 사용할 모듈의 가져오기에 추가합니다. (각도 기본 사항)...그런 다음 사용합니다.

<input formControlName="myValue" [yourPipeSelector]="'some args'" />

보일라

그러나 참고:사용자가 다시 편집할 수 있는 입력 요소를 조작합니다.값은 입력(및 입력 유형)과 호환되어야 합니다.

예: 예:만약 당신이 가지고 있다면.input[type="number"]그리고 DecimalPipe를 사용하면 다음과 같은 값 유형을 설정해야 합니다.number 컨롤로 ▁type의 유형이 string번호 파이프가 반환합니다.

또한 이 기능은 이벤트 방출(emitEvent)을 방지하지 않는 경우에만 작동합니다.그렇지 않으면 단순히 값을 설정한 위치에서 값을 변환해야 합니다.

updateOn 옵션도 확인합니다.FormControl예를 들어 다음과 같이 설정합니다.blur사용자 입력 중에 성가신 변경을 방지합니다.

두통을 줄이고 필요한 기능을 내보냅니다.내 경우:

export const formatDateTimePG = (datetime: any) => {
  const dt = new Date(datetime);
  const dtDateTime = new Date(dt.valueOf() + dt.getTimezoneOffset() * 60 * 1000);
  return format(dtDateTime, 'yyyy-MM-dd HH:mm:ss');
};

그러면 구성 요소에서 다음 작업을 수행합니다.

// TRANSFORM DATE IN FORM CONTROL FOR DISPLAY PURPOSE.
this.medicationForm.valueChanges.subscribe(val => {
 if (typeof val.medstartdate === 'string') {
   const maskedVal = formatDatePG(val.medstartdate);
    if (val.medstartdate !== maskedVal) {
        this.medicationForm.patchValue({ medstartdate: maskedVal });
    }
   }
 });
<input type="text" name="name" class="form-control mw-120 text-right"
 [value]="finalRow.controls[i].get('rental').value |currency">

formControlName으로 값을 재정의하지 않으므로 가장 효율적입니다.

formControlName을 추가하는 것은 항상 올바르게 작동하지 않으므로 주의해야 합니다.

<input type="text" name="name" class="form-control mw-120 text-right"
 [value]="finalRow.controls[i].get('rental').value |currency" formControlName="rental">

결과가 예상되지 않을 수 있습니다.

이 코드만 사용

<input [value]="value | udpCurrency"  name="inputField" type="number" formControlName="amount" />

언급URL : https://stackoverflow.com/questions/49522542/how-to-use-pipes-in-angular-5-reactive-form-input

반응형