웹사이트 검색

ControlValueAccessor를 사용하여 Angular에서 사용자 지정 양식 컨트롤을 만드는 방법


소개

Angular에서 양식을 만들 때 표준 텍스트 입력, 선택 또는 확인란이 아닌 입력을 원할 때가 있습니다. ControlValueAccessor 인터페이스를 구현하고 구성 요소를 NG_VALUE_ACCESSOR로 등록하면 사용자 지정 양식 컨트롤을 기본 입력인 것처럼 템플릿 기반 또는 반응형 양식에 원활하게 통합할 수 있습니다. !

이 문서에서는 기본 별 등급 입력 구성 요소를 ControlValueAccessor로 변환합니다.

전제 조건

이 자습서를 완료하려면 다음이 필요합니다.

  • Node.js를 로컬에 설치했습니다. Node.js를 설치하고 로컬 개발 환경을 만드는 방법에 따라 수행할 수 있습니다.
  • Angular 구성 요소 사용에 어느 정도 익숙하면 도움이 될 수 있습니다.

이 튜토리얼은 Node v16.4.2, npm v7.18.1, angular v12.1.1에서 확인되었습니다.

1단계 - 프로젝트 설정

먼저 새 RatingInputComponent를 만듭니다.

이것은 @angular/cli로 수행할 수 있습니다.

  1. ng generate component rating-input --inline-template --inline-style --skip-tests --flat --prefix

이렇게 하면 앱 declarations에 새 구성 요소가 추가되고 rating-input.component.ts 파일이 생성됩니다.

import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'rating-input',
  template: `
    <p>
      rating-input works!
    </p>
  `,
  styles: [
  ]
})
export class RatingInputComponent implements OnInit {

  constructor() { }

  ngOnInit(): void {
  }

}

템플릿, 스타일 및 논리를 추가합니다.

import { Component } from '@angular/core';

@Component({
  selector: 'rating-input',
  template: `
    <span
      <^>*ngFor="let starred of stars; let i = index"
      (click)="rate(i + (starred ? (value > i + 1 ? 1 : 0) : 1))"<^>
    >
      <ng-container *ngIf="starred; else noStar">⭐</ng-container>
      <ng-template #noStar>·</ng-template>
    </span>
  `,
  styles: [`
    span {
      display: inline-block;
      width: 25px;
      line-height: 25px;
      text-align: center;
      cursor: pointer;
    }
  `]
})
export class RatingInputComponent {
  stars: boolean[] = Array(5).fill(false);

  get value(): number {
    return this.stars.reduce((total, starred) => {
      return total + (starred ? 1 : 0);
    }, 0);
  }

  rate(rating: number) {
    this.stars = this.stars.map((_, i) => rating > i);
  }
}

구성 요소의 (0에서 5)을 가져오고 rate를 호출하여 구성 요소의 값을 설정할 수 있습니다. 코드> 기능을 사용하거나 원하는 별의 수를 클릭하십시오.

응용 프로그램에 구성 요소를 추가할 수 있습니다.

<rating-input></rating-input>

그리고 애플리케이션을 실행합니다.

  1. ng serve

그리고 웹 브라우저에서 상호 작용합니다.

이것은 훌륭하지만 이 입력을 양식에 추가하고 모든 것이 아직 작동하기를 기대할 수는 없습니다. ControlValueAccessor로 만들어야 합니다.

2단계 - 사용자 지정 양식 컨트롤 만들기

RatingInputComponent가 기본 입력(따라서 진정한 사용자 지정 양식 컨트롤)인 것처럼 동작하도록 하려면 Angular에 몇 가지 작업을 수행하도록 알려야 합니다.

  • 입력에 값 쓰기 - writeValue
  • 입력 값이 변경될 때 Angular에 알리는 함수 등록 - registerOnChange
  • 입력이 터치되었을 때 Angular에 알리는 함수 등록 - registerOnTouched
  • 입력 비활성화 - setDisabledState

이 네 가지가 ControlValueAccessor 인터페이스, 양식 컨트롤과 기본 요소 또는 사용자 지정 입력 구성 요소 사이의 브리지를 구성합니다. 구성 요소가 해당 인터페이스를 구현하면 사용할 수 있도록 NG_VALUE_ACCESSOR로 제공하여 Angular에 알려야 합니다.

코드 편집기에서 rating-input.component.ts를 다시 방문하여 다음과 같이 변경합니다.

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

@Component({
  selector: 'rating-input',
  template: `
    <span
      *ngFor="let starred of stars; let i = index"
      (click)="onTouched(); rate(i + (starred ? (value > i + 1 ? 1 : 0) : 1))"
    >
      <ng-container *ngIf="starred; else noStar">⭐</ng-container>
      <ng-template #noStar>·</ng-template>
    </span>
  `,
  styles: [`
    span {
      display: inline-block;
      width: 25px;
      line-height: 25px;
      text-align: center;
      cursor: pointer;
    }
  `],
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => RatingInputComponent),
      multi: true
    }
  ]
})
export class RatingInputComponent implements ControlValueAccessor {
  stars: boolean[] = Array(5).fill(false);

  // Allow the input to be disabled, and when it is make it somewhat transparent.
  @Input() disabled = false;
  @HostBinding('style.opacity')
  get opacity() {
    return this.disabled ? 0.25 : 1;
  }

  // Function to call when the rating changes.
  onChange = (rating: number) => {};

  // Function to call when the input is touched (when a star is clicked).
  onTouched = () => {};

  get value(): number {
    return this.stars.reduce((total, starred) => {
      return total + (starred ? 1 : 0);
    }, 0);
  }

  rate(rating: number) {
    if (!this.disabled) {
      this.writeValue(rating);
    }
  }

  // Allows Angular to update the model (rating).
  // Update the model and changes needed for the view here.
  writeValue(rating: number): void {
    this.stars = this.stars.map((_, i) => rating > i);
    this.onChange(this.value);
  }

  // Allows Angular to register a function to call when the model (rating) changes.
  // Save the function as a property to call later here.
  registerOnChange(fn: (rating: number) => void): void {
    this.onChange = fn;
  }

  // Allows Angular to register a function to call when the input has been touched.
  // Save the function as a property to call later here.
  registerOnTouched(fn: () => void): void {
    this.onTouched = fn;
  }

  // Allows Angular to disable the input.
  setDisabledState(isDisabled: boolean): void {
    this.disabled = isDisabled;
  }
}

이 코드는 입력을 비활성화할 수 있도록 하며 비활성화되면 어느 정도 투명하게 만듭니다.

애플리케이션을 실행합니다.

  1. ng serve

그리고 웹 브라우저에서 상호 작용합니다.

입력 컨트롤을 비활성화할 수도 있습니다.

<rating-input [disabled]="true"></rating-input>

이제 RatingInputComponent가 사용자 지정 양식 구성 요소라고 말할 수 있습니다! 템플릿 기반 또는 반응형 형식에서 다른 기본 입력(Angular는 ControlValueAccessors을 제공합니다!)과 동일하게 작동합니다.

결론

이 문서에서는 기본 별 등급 입력 구성 요소를 ControlValueAccessor로 변환했습니다.

이제 다음을 알 수 있습니다.

  • ngModel은 "작동\합니다.
  • 맞춤형 유효성 검사를 추가할 수 있습니다.
  • 제어 상태 및 유효성은 ng-dirtyng-touched 클래스와 같은 ngModel에서 사용할 수 있습니다.

Angular에 대해 자세히 알아보려면 연습 및 프로그래밍 프로젝트에 대한 Angular 주제 페이지를 확인하세요.