Angular에서 재사용 가능한 다중 검사 필드 만들기
35335 단어 angularformswebdevjavascript
일반적으로 사용자는 여러 가지 사용 가능한 양식에서 여러 가지 옵션을 선택할 수 있습니다.
Angular에서 이 문제를 처리하는 가장 일반적인 방법은 한 그룹
<input type="checkbox">
과 한 그룹FormArray
을 사용하는 것이다.그러나 응용 프로그램의 몇 가지 형식이 같은 기능을 필요로 할 때, 우리는 논리와 표기를 포함한 대량의 코드를 반복하기 시작할 가능성이 높다.본 논문에서는 다음과 같은 특성을 가진 구성 요소를 구축하여 이 문제를 해결하고자 합니다.
카탈로그
설계
우리의 구성 요소는 두 개의 요소로 구성된다.
1단계: SimpleCheckOption 구성 요소 지원
우선, 우리는
simple-check-option
을 통해 multi-check-field
만 지원하지만, 이 필드는 모든 옵션 구성 요소와 함께 사용하기를 희망한다는 것을 기억하십시오.즉, 다음과 같이 컨텐츠 투영을 사용하여
multi-check-field
에 필요한 옵션을 제공합니다.<multi-check-field>
<simple-check-option *ngFor="let option of options" [value]="option"
[label]="option.label">
</single-check-option>
</multi-check-field>
multi-check-field
닫힌 태그 내에서 컨텐트 투영 옵션을 전달하는 방법에 유의하십시오.이제
simple-check-option
의 실현을 살펴보겠습니다.@Component({
selector: 'simple-check-option',
template: `
<label>
<input type="checkbox" [formControl]="control">
{{ label }}
</label>
`
})
export class SimpleCheckOptionComponent {
@Input() value: any;
@Input() label: string;
public control = new FormControl(false);
get valueChanges$(): Observable<boolean> {
return this.control.valueChanges;
}
}
이 구성 요소는 라벨이 있는 표준<input type="checkbox">
이 있습니다.또한 확인란 값을 조작하기 위해 FormControl
를 설명했고, 외부에서 유형 보안이 있는 구성 요소와 상호작용할 수 있도록 valueChanges$
접근기를 제공했다.multi-check-field
구성 요소는 ContentChildren
데코더를 사용하여 투영 옵션을 조회합니다.@Component({
selector: 'multi-check-field',
template: `<ng-content></ng-content>`
})
export class MultiCheckFieldComponent implements AfterContentInit {
@ContentChildren(SimpleCheckOptionComponent)
options!: QueryList<SimpleCheckOptionComponent>;
ngAfterContentInit(): void {
// Content query ready
}
}
주의해야 할 것은 내용 조회는 우선 AfterContentInit
생명주기에 사용할 준비가 되어 있지만 이전은 아니다.또한 구성 요소 템플릿의 <ng-content>
태그를 사용하여 제공된 내용 (옵션) 을 표시하는 방법을 참조하십시오.이제 선택한 옵션을 추적하는 방법을 살펴보겠습니다.
private subscriptions = new Subscription();
private selectedValues: any[] = [];
ngAfterContentInit(): void {
this.options.forEach(option => {
this.subscriptions.add(
option.valueChanges$.subscribe(
(optionChecked) => {
if (optionChecked) {
this.add(option.value);
} else {
this.remove(option.value);
}
}
)
);
});
}
private add(value: any): void {
this.selectedValues.push(value);
}
private remove(value: any): void {
const idx = this.selectedValues.findIndex(v => v === value);
if (idx >= 0) {
this.selectedValues.splice(idx, 1);
}
}
옵션을 선택/선택 취소할 때 옵션의 valueChanges$
방문자 구독 이벤트를 사용합니다.optionChecked
부울 값에 따라 이 옵션을 selectedValues
그룹에서 계속 추가하거나 삭제합니다.이때 우리의
multi-check-field
와 simple-check-option
는 완전히 통합되었다.그러나 Angular 컨텐트 투영을 사용하여 검사 옵션으로 모든 구성 요소를 지원해야 합니다.어떻게 하는지 봅시다.2단계: 모든 유형의 옵션 구성 요소 지원
simple-check-option
와 매우 다르지만 같은 기능을 가진 새로운 옵션 구성 요소를 만듭니다.우리는 그것을 user-check-option
라고 명명했다. 그것은 대표할 것이다.응, 한 사용자😅.구성 요소 논리는 기본적으로
simple-check-option
와 같지만 템플릿에 큰 차이가 있습니다.@Component({
selector: 'user-check-option',
template: `
<label>
<input type="checkbox" [formControl]="control">
<div class="card">
<div class="avatar">
<img src="assets/images/{{ value.avatar }}">
<div class="span"></div>
</div>
<h1>{{ value.name }}</h1>
<h2>{{ value.location }}</h2>
</div>
</label>
`
})
export class UserCheckOptionComponent {
@Input() value: any;
public control = new FormControl(false);
get valueChanges$(): Observable<boolean> {
return this.control.valueChanges;
}
}
필드 구성 요소를 통해 새로운 user-check-option
쿼리를 지원하기 위해서, 우리는 더 이상 ContentChildren
쿼리를 수정해야 합니다.이것은 우리의 현재 조회입니다.@ContentChildren(SimpleCheckOptionComponent)
options!: QueryList<SimpleCheckOptionComponent>;
불행하게도 우리는 두 가지 다른 구성 요소에 대해 SimpleCheckOption
를 사용할 수 없지만, Angular's Dependency Injection (DI) 의 기능을 사용하여 이런 상황을 극복할 수 있다.의존 주입 구조👨🚒 👩🚒 🚒
이 문제의 가능한 해결 방안은 alias providers 를 사용하여 우리의 옵션 구성 요소가 사용할 수 있도록 공공 DI token 을 만드는 것이다.
abstract class MultiCheckOption { } // (1)
@Component({
selector: 'simple-check-option',
providers: [
{ // (2)
provide: MultiCheckOption,
useExisting: SimpleCheckOptionComponent,
}
]
})
export class SimpleCheckOptionComponent { ... }
@Component({
selector: 'user-check-option',
providers: [
{ // (3)
provide: MultiCheckOption,
useExisting: UserCheckOptionComponent
}
]
})
export class UserCheckOptionComponent { ... }
ContentChildren
클래스를 만듭니다.MultiCheckOption
구성 요소 수준에서 분유기를 설정합니다.이 설정에서 Angular의 DI가 구성 요소의 주입기에 요청SimpleCheckOptionComponent
의 실례를 요청하면 구성 요소 자체의 기존 실례를 전달합니다.MultiCheckOption
이렇게 한다.UserCheckOptionComponent
현재 검색어는 다음과 같이 다시 쓸 수 있습니다.@ContentChildren(MultiCheckOption)
options!: QueryList<MultiCheckOption>;
하지만 우리는 아직 끝나지 않았어...이 때, 옵션 구성 요소의 구성원과 방법에 접근할 수 없습니다. 왜냐하면 ContentChildren
클래스가 비어 있기 때문입니다.우리는 클래스 자체를 사용하여 옵션에서 흔히 볼 수 있는 내용을 저장하고 필요한 내용을 공개함으로써 이 문제를 해결할 수 있다.그 후에 우리는 ES6 클래스 계승을 이용하여 MultiCheckOption
에서 option
구성 요소를 확장했다.export abstract class MultiCheckOption {
abstract value: any;
public control = new FormControl(false);
get valueChanges$(): Observable<boolean> {
return this.control.valueChanges;
}
}
@Component(...)
export class SimpleCheckOptionComponent extends MultiCheckOption {
@Input() value: any;
@Input() label: string;
}
@Component(...)
export class UserCheckOptionComponent extends MultiCheckOption {
@Input() value: any;
}
그렇듯이, MultiCheckOption
논리를 실현하는 모든 구성 요소를 지원합니다.3단계: 각도 형태와의 통합
이 단계에서
multi-check-field
각도와 결합하여 사용할 수 있습니다<multi-check-field formControlName="subjects">
...
</multi-check-field>
하지만 다음과 같은 오류가 발생합니다.No value accessor for form control with name: 'subjects'
원인은
MultiCheckOption
원생 형식 원소(예를 들어 multi-check-field
와 AngularFormsModule
만 어떻게 처리하는지 알기 때문이다.우리의 맞춤형<input>
이 각도 형식을 처리할 수 있도록 프레임워크와 어떻게 통신하는지 알려야 한다.(Angular의 사용자 정의 폼 필드를 처음 들어보셨다면 post 를 확인해 보시기 바랍니다.1.select 공급자
우리는 먼저 전 세계
multi-check-field
공급업체에 구성 요소를 등록합니다.import { Component, forwardRef } from '@angular/core';
import { NG_VALUE_ACCESSOR } from '@angular/forms';
@Component({
selector: 'multi-check-field',
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => MultiCheckFieldComponent),
multi: true
}
]
})
export class MultiCheckFieldComponent { ... }
이.NG\u VALUE\u 액세스기 인터페이스
그 밖에 우리는
NG_VALUE_ACCESSOR
인터페이스를 실현해야 한다. 이 인터페이스는 보기 (우리의 구성 요소) 와 모델 (표 컨트롤) 의 동기화를 유지하기 위해 다음과 같은 방법 집합을 정의했다.writeValue(obj: any): void;
registerOnChange(fn: any): void;
registerOnTouched(fn: any): void;
setDisabledState?(isDisabled: boolean): void;
writeValue(객체: 임의)
이 함수는 모델에서 보기까지의 필드 값을 설정하기 위해 프레임에서 실행됩니다.예를 들어, 다음 작업을 수행할 때
multiCheckControl = new FormControl(TEST_INITIAL_VALUE);
multiCheckControl.setValue(TEST_VALUE);
multiCheckControl.patchValue(TEST_VALUE);
우리의 예에서, ControlValueAccesor
매개 변수는 선택한 옵션 값을 포함하는 그룹이어야 한다.가독성을 높이기 위해서 우리는 그것을 ControlValueAccesor
라고 명명하는 것이 가장 좋다.writeValue(values: any[]): void {
this.selectedValues = [];
values = values || [];
values.forEach(selectedValue => {
const selectedOption = this.options.find(v => v.value === selectedValue);
selectedOption.control.setValue(true);
});
}
obj
수조의 모든 항목이 상응하는 values
에 비추고 검사 값이 보기에 반영된다. (우리의 예시에서 이것은 다른 컨트롤러를 통해 이루어진다.)values
를 호출할 때마다 option
에서 설명한 상응하는 selectedOption.control.setValue()
구독을 호출하고 옵션의 값을 로컬valueChanges$
그룹에 추가합니다.우리 그것의 일을 좀 봅시다
@Component({
selector: 'app-root',
template: `
<multi-check-field [formControl]="multiCheckControl">
<simple-check-option *ngFor="let subject of subjects"
[value]="subject" [label]="subject.label">
</simple-check-option>
</multi-check-field>
<button (click)="setTestValue()">Set Test Value</button>
Control value: <pre>{{ multiCheckControl.value | json }}</pre>
`,
})
export class AppComponent {
public subjects = [
{ code: '001', label: 'Math' },
{ code: '002', label: 'Science' },
{ code: '003', label: 'History' },
];
public multiCheckControl = new FormControl();
setTestValue() {
const testValue = [this.subjects[0], this.subjects[1]];
this.multiCheckControl.setValue(testValue);
}
}
레지스터 변경 (fn: 모두)
UI에서 필드 값이 변경될 때 호출해야 하는 함수를 등록합니다.제공된 함수를 호출하면 보기의 값이 모델에 업데이트됩니다.
우리의 예에서 옵션을 선택하거나 취소할 때마다 모델 값을 업데이트해야 합니다.
export class MultiCheckFieldComponent implements ControlValueAccessor {
_onChange: (_: any) => void;
registerOnChange(fn: any): void {
this._onChange = fn;
}
private add(value: any): void {
this.selectedValues.push(value);
this._onChange(this.selectedValues);
}
private remove(value: any): void {
const idx = this.selectedValues.findIndex(v => v === value);
if (idx >= 0) {
this.selectedValues.splice(idx, 1);
this._onChange(this.selectedValues);
}
}
...
}
레지스터 터치 (fn: 모두)
앞의 방법과 마찬가지로, 컨트롤러가 검증을 촉발할 수 있도록 터치 필드에 호출할 함수를 등록해야 합니다.
우리는 이런 방법의 실현을 본 강좌의 범위에서 제외할 것이다.
비활성화 상태를 설정합니까?(isDisabled: 부울)
마지막으로 가장 중요하지 않은 것은
ngAfterContentInit
방법이다.필드를 프로그래밍 방식으로 사용하거나 사용하지 않을 때 이 함수를 호출합니다.예를 들어, 다음 작업을 수행할 때multiCheckControl = new FormControl({
value: TEST_INITIAL_VALUE,
disabled: true
});
multiCheckControl.disabled();
multiCheckControl.enabled();
이 방법도 본 강좌의 범위 내에 있지 않을 것이다.마지막 한마디
우리는 다중 검사 기능을 제공하는 구성 요소를 만들려고 노력했지만 다음과 같은 기능도 제공했다.
selectedValues
과 유사하며 내부에 setDisabledState
개의 라벨이 있습니다.프레젠테이션 시간🌋
한층 더 개선하다
아직 많은 개선 공간이 있다.코드를 작성해야 할 경우에 대비해 나는 여기에 몇 가지 생각을 열거했다.스토리지 라이브러리에 솔루션을 통합하기 위해 PR을 원활하게 실행합니다.
코드 저장소 링크
Reference
이 문제에 관하여(Angular에서 재사용 가능한 다중 검사 필드 만들기), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/jcamilom/building-a-reusable-multicheck-field-in-angular-498h텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)