Angular

Angular - ControlValueAccessor

인어공쭈 2024. 9. 12. 18:02

Angular에서 ControlValueAccessor 쉽게 이해하기

Angular에서 사용자 정의 폼 컴포넌트를 만들다 보면, Angular의 폼 시스템과 연동해야 하는 경우가 있다. 그때 필요한 것이 바로 ControlValueAccessor이다. 이 인터페이스를 사용하면 Angular 폼 제어기(FormControl, NgModel)가 커스텀 컴포넌트와 원활하게 연결될 수 있다. 쉽게 말해, Angular의 기본 폼 요소(input, select 등)처럼 커스텀 컴포넌트도 동일하게 동작하도록 만들어 주는 역할을 한다.

 

Angular에서 커스텀 폼 컴포넌트가 Angular 폼 제어기(FormControl, NgModel)와 상호작용하는 과정은 ControlValueAccessor의 메서드들을 통해 이루어진다. 이 메서드들이 호출되는 순서를 보면 쉽게 이해할 수 있다.

  1. 폼 컨트롤 초기화
    • 폼이 처음 렌더링될 때 Angular 폼 제어기가 연결된 커스텀 컴포넌트에 값을 전달한다.
    • 이때 호출되는 메서드가 writeValue(value)이다. Angular 폼이 가진 초기 값이 컴포넌트에 설정된다.
  2. 값 변경 시
    • 사용자가 커스텀 폼 컴포넌트에서 값을 입력하면, 해당 값이 컴포넌트 내부에서 변경된다.
    • 이때 커스텀 컴포넌트는 registerOnChange(fn)으로 등록된 함수를 호출하여 Angular 폼 제어기에 값 변경을 알린다. Angular는 이 함수로 값을 받아 폼의 상태를 업데이트한다.
  3. 터치 이벤트
    • 사용자가 컴포넌트와 상호작용하여 폼 필드가 '터치'되면, 즉 포커스가 갔다가 나오는 경우 registerOnTouched(fn)으로 등록된 콜백이 호출된다. Angular 폼 제어기는 이를 통해 폼 필드가 터치되었음을 알 수 있다.
  4. 폼 비활성화
    • Angular 폼에서 커스텀 컴포넌트를 비활성화하거나 활성화하는 경우, setDisabledState(isDisabled) 메서드가 호출된다. 이 메서드는 컴포넌트의 비활성화 여부를 받아서 이를 반영하도록 한다.

 

ControlValueAccessor 구현

import { Component, forwardRef } from '@angular/core';
import { NG_VALUE_ACCESSOR, ControlValueAccessor } from '@angular/forms';

@Component({
  selector: 'app-custom-input',
  template: `
    <input type="text" [value]="value" (input)="onInput($event)" (blur)="onBlur()">
  `,
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => CustomInputComponent),
      multi: true
    }
  ]
})
export class CustomInputComponent implements ControlValueAccessor {
  value: string = '';

  // 저장된 onChange와 onTouched 콜백 함수
  private onChange: (value: string) => void = () => {};
  private onTouched: () => void = () => {};

  // 1. writeValue - 폼에서 전달된 값을 컴포넌트에 설정
  writeValue(value: string): void {
    this.value = value;
  }

  // 2. registerOnChange - 폼에서 값이 변경될 때 호출할 콜백 등록
  registerOnChange(fn: any): void {
    this.onChange = fn;
  }

  // 3. registerOnTouched - 폼에서 필드가 터치되었을 때 호출할 콜백 등록
  registerOnTouched(fn: any): void {
    this.onTouched = fn;
  }

  // 4. setDisabledState - 폼의 활성/비활성 상태를 반영
  setDisabledState(isDisabled: boolean): void {
    // 필요에 따라 컴포넌트의 활성화/비활성화 처리
  }

  // 입력 값이 변경될 때 호출되는 함수
  onInput(event: Event): void {
    const value = (event.target as HTMLInputElement).value;
    this.value = value;
    this.onChange(value); // 폼에 값 변경을 알림
  }

  // 입력 필드가 터치되었을 때 호출
  onBlur(): void {
    this.onTouched(); // 폼에 터치 상태를 알림
  }
}

//사용자 정의 컴포넌트 사용
<form #myForm="ngForm">
  <app-custom-input name="customInput" [(ngModel)]="myValue"></app-custom-input>
</form>

<p>Current value: {{ myValue }}</p>

 

값이 입력되고 부모로 전달되는 흐름

1. 사용자가 입력 필드에 값을 입력
사용자가 커스텀 입력 컴포넌트에서 값을 입력하면, 그 값은 컴포넌트 내부의 onInput 함수에서 처리된다. 이 함수는 (input) 이벤트와 연결되어 있어, 입력이 일어날 때마다 호출된다.

2. onInput에서 값 변경 처리
onInput 함수는 사용자가 입력한 값을 받아서 컴포넌트의 value 변수에 저장한 후, this.onChange(value)를 호출한다. 여기서 onChange는 registerOnChange 메서드를 통해 Angular 폼 시스템에서 등록한 콜백 함수다.

onInput(event: Event): void {
  const value = (event.target as HTMLInputElement).value;
  this.value = value;
  this.onChange(value); // 이 부분이 부모인 Angular 폼에 값을 전달하는 핵심
}

 

3. registerOnChange에서 콜백 등록
Angular 폼 시스템은 커스텀 컴포넌트와 상호작용하기 위해, registerOnChange 메서드를 호출하면서 콜백 함수를 등록한다. 이 함수는 값이 변경될 때마다 실행되도록 커스텀 컴포넌트에서 관리되며, onChange 콜백이 호출되면 Angular는 내부적으로 폼의 값을 갱신한다.이 과정을 통해, 컴포넌트에서 값이 변경될 때마다 fn, 즉 Angular의 폼 시스템에 등록된 함수가 호출되고, 이 함수는 폼 전체의 상태를 업데이트한다.

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

 

4. 부모(Angular 폼)가 값을 받음
결국 this.onChange(value) 호출 시 등록된 함수가 호출되면, Angular 폼 시스템은 새로운 값을 감지하고 해당 폼 컨트롤의 값을 업데이트한다. 즉, 부모 컴포넌트나 폼 그룹, 폼 배열 등 Angular의 폼 구조가 이 값을 반영하게 된다.

 

 

결론

요약하자면, 커스텀 입력 컴포넌트에서 값이 변경되면:

  1. 사용자가 입력한 값이 onInput 함수를 통해 잡힌다.
  2. 그 값은 onChange 콜백 함수로 전달되고,
  3. 이 콜백 함수는 registerOnChange로 Angular 폼 시스템에 등록된 함수로서 폼의 상태를 업데이트한다.
  4. 최종적으로 부모 컴포넌트나 폼 컨트롤이 해당 값의 변화를 감지하고 업데이트된다.

 

 

반응형