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
인터페이스를 구현하여 폼 컨트롤의 값을 읽고 쓰며 유효성 검사를 수행할 수 있습니다.
코드의 주요 요소를 살펴보면 다음과 같습니다:
CustomValueAccessor<T>
클래스: 제네릭 타입 T
를 받는 커스텀 값 접근자 클래스를 선언합니다.
disabled
, required
, touched
등의 프로퍼티: 컨트롤의 상태 및 속성을 추적하기 위한 프로퍼티들입니다.
onChange
, onTouched
, onValidationchange
등의 콜백 함수: 값이 변경되거나 터치되었을 때 호출되는 콜백 함수들입니다.
_value
프로퍼티: 실제 값이 저장되는 프로퍼티입니다.
value
getter와 setter: _value
프로퍼티에 접근하여 값을 가져오거나 설정합니다. 값이 변경될 때마다 onChange
콜백 함수와 onTouched
콜백 함수를 호출하고 touched
프로퍼티를 true
로 설정합니다.
writeValue(value: T)
: 외부에서 값이 변경되었을 때 호출되는 메서드로, 값을 받아서 value
setter를 통해 내부 _value
프로퍼티에 설정합니다.
registerOnChange(fn: (value: T) => {})
와 registerOnTouched(fn: () => {})
: 외부에서 콜백 함수를 등록할 수 있도록 하는 메서드들입니다.
setDisabledState(isDisabled: boolean)
: 폼 컨트롤의 활성화/비활성화 상태를 설정하는 메서드입니다.
validate(control: AbstractControl<any, any>)
: 폼 컨트롤의 유효성을 검사하는 메서드로, 필수 필드인 경우 값이 없을 때 required: true
를 반환하고, 그렇지 않은 경우 null
을 반환합니다.
registerOnValidatorChange(fn: () => void)
: 유효성 검사가 변경될 때 호출되는 콜백 함수를 등록하는 메서드입니다.
다음은 CustomValueAccessor
를 사용하는 몇 가지 예시입니다:
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>();
// ...
}
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>();
// ...
}
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>();
// ...
}
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
클래스의 메서드와 프로퍼티가 해당 폼 요소와 상호 작용하고 값 및 상태 변경을 추적할 수 있습니다.