Angular CustomValueAccessor ControlValueAccessor

agnusdei·2023년 7월 7일
0
import { AbstractControl, ControlValueAccessor, NG_VALUE_ACCESSOR, ValidationErrors, Validator } from "@angular/forms";

export class CustomValueAccessor<T> implements ControlValueAccessor, Validator {
    disabled = false;
    required: boolean = false;
    touched: boolean = false;
    onChange: any = (value: T) => { };
    onTouched: any = () => { };
    onValidationchange: any = () => { };

    private _value!: T;

    set value(newValue: T) {
        if (this._value === newValue) { return; }
        this._value = newValue;
        this.onChange(newValue);
        this.onTouched();
        this.touched = true
    }

    get value(): T {
        return this._value;
    }

    writeValue(value: T): void {
        this.value = value;
    }

    registerOnChange(fn: (value: T) => {}): void {
        this.onChange = fn;
    }

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

    setDisabledState?(isDisabled: boolean): void {
        this.disabled = isDisabled;
    }

    validate(control: AbstractControl<any, any>): ValidationErrors | null {
        return this.required && this.touched ? !this._value ? { required: true } : null : null
    }

    registerOnValidatorChange?(fn: () => void): void {
        this.onValidationchange = fn
    }

}

이 클래스는 ControlValueAccessor 인터페이스와 Validator 인터페이스를 구현하여 폼 컨트롤의 값을 읽고 쓰며 유효성 검사를 수행할 수 있습니다.

코드의 주요 요소를 살펴보면 다음과 같습니다:

  1. CustomValueAccessor<T> 클래스: 제네릭 타입 T를 받는 커스텀 값 접근자 클래스를 선언합니다.

  2. disabled, required, touched 등의 프로퍼티: 컨트롤의 상태 및 속성을 추적하기 위한 프로퍼티들입니다.

  3. onChange, onTouched, onValidationchange 등의 콜백 함수: 값이 변경되거나 터치되었을 때 호출되는 콜백 함수들입니다.

  4. _value 프로퍼티: 실제 값이 저장되는 프로퍼티입니다.

  5. value getter와 setter: _value 프로퍼티에 접근하여 값을 가져오거나 설정합니다. 값이 변경될 때마다 onChange 콜백 함수와 onTouched 콜백 함수를 호출하고 touched 프로퍼티를 true로 설정합니다.

  6. writeValue(value: T): 외부에서 값이 변경되었을 때 호출되는 메서드로, 값을 받아서 value setter를 통해 내부 _value 프로퍼티에 설정합니다.

  7. registerOnChange(fn: (value: T) => {})registerOnTouched(fn: () => {}): 외부에서 콜백 함수를 등록할 수 있도록 하는 메서드들입니다.

  8. setDisabledState(isDisabled: boolean): 폼 컨트롤의 활성화/비활성화 상태를 설정하는 메서드입니다.

  9. validate(control: AbstractControl<any, any>): 폼 컨트롤의 유효성을 검사하는 메서드로, 필수 필드인 경우 값이 없을 때 required: true를 반환하고, 그렇지 않은 경우 null을 반환합니다.

  10. registerOnValidatorChange(fn: () => void): 유효성 검사가 변경될 때 호출되는 콜백 함수를 등록하는 메서드입니다.

다음은 CustomValueAccessor를 사용하는 몇 가지 예시입니다:

  1. Input Text 사용 예시:
import { Component } from "@angular/core";

@Component({
  selector: "app-custom-input",
  template: `
    <input type="text" [(ngModel)]="textValue" [customValueAccessor]="textAccessor" />
  `,
})
export class CustomInputComponent {
  textValue: string;
  textAccessor = new CustomValueAccessor<string>();

  // ...
}
  1. Radio Button 사용 예시:
import { Component } from "@angular/core";

@Component({
  selector: "app-custom-radio",
  template: `
    <label>
      <input type="radio" [(ngModel)]="radioValue" value="option1" [customValueAccessor]="radioAccessor" />
      Option 1
    </label>
    <label>
      <input type="radio" [(ngModel)]="radioValue" value="option2" [customValueAccessor]="radioAccessor" />
      Option 2
    </label>
  `,
})
export class CustomRadioComponent {
  radioValue: string;
  radioAccessor = new CustomValueAccessor<string>();

  // ...
}
  1. Checkbox 사용 예시:
import { Component } from "@angular/core";

@Component({
  selector: "app-custom-checkbox",
  template: `
    <label>
      <input type="checkbox" [(ngModel)]="checkboxValue" [customValueAccessor]="checkboxAccessor" />
      Checkbox
    </label>
  `,
})
export class CustomCheckboxComponent {
  checkboxValue: boolean;
  checkboxAccessor = new CustomValueAccessor<boolean>();

  // ...
}
  1. FormGroup과 함께 사용하는 예시:
import { Component } from "@angular/core";
import { FormBuilder, FormGroup } from "@angular/forms";

@Component({
  selector: "app-custom-form",
  template: `
    <form [formGroup]="form">
      <input type="text" formControlName="name" [customValueAccessor]="nameAccessor" />
      <input type="checkbox" formControlName="agree" [customValueAccessor]="agreeAccessor" />
    </form>
  `,
})
export class CustomFormComponent {
  form: FormGroup;
  nameAccessor = new CustomValueAccessor<string>();
  agreeAccessor = new CustomValueAccessor<boolean>();

  constructor(private formBuilder: FormBuilder) {
    this.form = this.formBuilder.group({
      name: "",
      agree: false,
    });
  }

  // ...
}

위 예시에서는 customValueAccessor 디렉티브를 사용하여 각각의 폼 요소와 CustomValueAccessor 인스턴스를 연결합니다. 이를 통해 CustomValueAccessor 클래스의 메서드와 프로퍼티가 해당 폼 요소와 상호 작용하고 값 및 상태 변경을 추적할 수 있습니다.

0개의 댓글