ANGULAR

[Angular]Reactive Form 정리

Angular

[Angular]Reactive Form 정리

Reactive Form 이란

반응형 폼(Reactive Form)이란 Form에서 받아오는 데이터의 변화를 감지하여 항상 새로운 상태의 데이터를 표시할 수 있게 만들어주는 방법이다.

Reactive Form API

클래스
클래스 설명
AbstractControl 폼 컨트롤을 표현하는 FormControl, FormGroup, FormArray의 추상 클래스로 폼 컨트롤의 공통 기능과 프로퍼티를 정의한다.
FormControl 개별 폼 컨트롤의 값과 유효성 검사 상태를 관리하는 클래스로 HTML 문서의 <input> 혹은 <select> 엘리먼트와 연동된다.
FormGroup 연관된 AbstractControl 인스턴스를 그룹으로 관리할 때 사용하는 클래스로 자식 폼 컨트롤을 프로퍼티로 관리하며, 그룹에 접근해도 자식 폼 컨트롤의 값이나 유효성 검사 상태도 확인할 수 있다.
FormArray AbstractControl을 배열 형태로 관리할 때 사용하는 클래스다.
FormBuilder 폼 컨트롤 인스턴스를 간편하게 만들때 사용하는 서비스다
디렉티브
디렉티브 설명
FormControlDirective 개별 FormControl 인스턴스와 폼 컨트롤 엘리먼트를 연결
FormControlName 이름으로 기준으로 FormGroup 안에 있는 FormControl을 연결
FormGroupDirective FormGroup 인스턴스를 DOM 엘리먼트와 연결
FormGroupName 중첩된 FormGroup 인스턴스를 DOM 엘리먼트와 연결
FormArrayName FormArray 인스턴스를 DOM 엘리먼트와 연결

FormControl

개별 폼 컨트롤의 값과 유효성 검사 상태를 관리하는 클래스로 HTML 문서의 <input> 혹은 <select> 엘리먼트와 연동된다.

FormControl 추가하기

  1. 앱에 반응형 폼 모듈을 로드
    import { ReactiveFormsModule } from '@angular/forms';
    
    @NgModule({
      imports: [
        // 다른 모듈들...
        ReactiveFormsModule
      ],
    })
    export class AppModule { }
  2. 컴포넌트에 FormControl 인스턴스를 정의
    import { Component } from '@angular/core';
    import { FormControl } from '@angular/forms';
    
    @Component({
      selector: 'app-name-editor',
      templateUrl: './name-editor.component.html',
      styleUrls: ['./name-editor.component.css']
    })
    export class NameEditorComponent {
      name = new FormControl('');
    }
  3. 템플릿에 FormControl을 등록
    <label for="name">Name: </label>
    <input id="name" type="text" [formControl]="name">
  4. 컴포넌트 표시
    <app-name-editor></app-name-editor>

FormControl 표시하기

<p>Value: {{ name.value }}</p>

폼 컨트롤 엘리먼트의 값이 변경될 때마다 자동으로 갱신된다.

FormControl 변경하기

setValue()

반응형 폼에서 제공하는 메소드 setValue() 메소드를 사용하면 폼 컨트롤의 값을 변경할 수 있으며,   유효성 검사도 함께 수행 해주기 때문에 현재 폼 컨트롤의 값 전체를 한 번에 변경할 수 있다.

updateName() {
  this.name.setValue('Nancy');
}
<button (click)="updateName()">Update Name</button>

이 때 폼 컨트롤의 값은 폼 모델과 연결되어 있기 때문에, 버튼을 눌러서 폼 컨트롤의 값이 변경되면 컴포넌트 클래스에 있는 폼 모델 값도 함께 변경된다.

FormGroup

연관된 AbstractControl 인스턴스를 그룹으로 관리할 때 사용하는 클래스로 자식 폼 컨트롤을 프로퍼티로 관리하며, 그룹에 접근해도 자식 폼 컨트롤의 값이나 유효성 검사 상태도 확인할 수 있다.

FormControl과 마찬가지로 valueuntouched 프로퍼티, setValue() 메소드를 제공한다.

Single FormGroup

  1. @angular/forms 패키지에서 재공하는 심볼 로드
    import { FormGroup, FormControl } from '@angular/forms';
  2. FormGroup 인스턴스를 추가
    import { Component } from '@angular/core';
    import { FormGroup, FormControl } from '@angular/forms';
    
    @Component({
      selector: 'app-profile-editor',
      templateUrl: './profile-editor.component.html',
      styleUrls: ['./profile-editor.component.css']
    })
    export class ProfileEditorComponent {
      profileForm = new FormGroup({
        firstName: new FormControl(''),
        lastName: new FormControl(''),
      });
    }
  3. FormGroup 모델과 화면을 연결
    <form [formGroup]="profileForm">
    
      <label for="first-name">First Name: </label>
      <input id="first-name" type="text" formControlName="firstName">
    
      <label for="last-name">Last Name: </label>
      <input id="last-name" type="text" formControlName="lastName">
    
    </form>
  4. 폼 데이터를 저장합니다.
    <form [formGroup]="profileForm" (ngSubmit)="onSubmit()">
    
    <p>Complete the form to enable button.</p>
    <button type="submit" [disabled]="!profileForm.valid">Submit</button>
    onSubmit() {
      // TODO: EventEmitter로 폼 내용 보내기
      console.warn(this.profileForm.value);
    }
    <app-profile-editor></app-profile-editor>

Nesting Form Group

FormGroup은 FormGroup안에 있는 개별 FormControl 인스턴스에 직접 접근할 수 있어 폼 그룹을 논리적으로 구성하면 폼 전체를 한 번에 관리하기 편해진다.

import { Component } from '@angular/core';
import { FormGroup, FormControl } from '@angular/forms';

@Component({
  selector: 'app-profile-editor',
  templateUrl: './profile-editor.component.html',
  styleUrls: ['./profile-editor.component.css']
})
export class ProfileEditorComponent {
  profileForm = new FormGroup({
    firstName: new FormControl(''),
    lastName: new FormControl(''),
    address: new FormGroup({
      street: new FormControl(''),
      city: new FormControl(''),
      state: new FormControl(''),
      zip: new FormControl('')
    })
  });
}
<div formGroupName="address">
  <h2>Address</h2>

  <label for="street">Street: </label>
  <input id="street" type="text" formControlName="street">

  <label for="city">City: </label>
  <input id="city" type="text" formControlName="city">

  <label for="state">State: </label>
  <input id="state" type="text" formControlName="state">

  <label for="zip">Zip Code: </label>
  <input id="zip" type="text" formControlName="zip">
</div>
patchValue()

setValue() 메소드를 사용하면 인자의 형태가 복잡한 폼 구조에 맞을 때만 값을 변경할 수 있지만 patchValue()메소드는 폼 그룹의 구조와 다른 인자가 전달되어도 에러를 발생하지 않는다.

updateProfile() {
  this.profileForm.patchValue({
    firstName: 'Nancy',
    address: {
      street: '123 Drew Street'
    }
  });
}
<button (click)="updateProfile()">Update Profile</button>

FormBuilder

FormBuilder는 직접 생성하지 않고 FormControl 인스턴스를 간편하게 만들때 사용하는 서비스다.

  1. FormBuilder 클래스를 로드
    import { FormBuilder } from '@angular/forms';
  2. FormBuilder 서비스를 의존성으로 주입
    constructor(private fb: FormBuilder) { }
  3. FormBuilder 서비스로 폼을 구성
    import { Component } from '@angular/core';
    import { FormBuilder } from '@angular/forms';
    
    @Component({
      selector: 'app-profile-editor',
      templateUrl: './profile-editor.component.html',
      styleUrls: ['./profile-editor.component.css']
    })
    export class ProfileEditorComponent {
      profileForm = this.fb.group({
        firstName: [''],
        lastName: [''],
        address: this.fb.group({
          street: [''],
          city: [''],
          state: [''],
          zip: ['']
        }),
      });
    
      constructor(private fb: FormBuilder) { }
    }
FormControl을 직접 생성하는 것과 FormBuild로 생성하는 것의 차이
profileForm = new FormGroup({
  firstName: new FormControl(''),
  lastName: new FormControl(''),
  address: new FormGroup({
    street: new FormControl(''),
    city: new FormControl(''),
    state: new FormControl(''),
    zip: new FormControl('')
  })
});
profileForm = this.fb.group({
  firstName: [''],
  lastName: [''],
  address: this.fb.group({
    street: [''],
    city: [''],
    state: [''],
    zip: ['']
  }),
});

FormArray

AbstractControl을 배열 형태로 관리할 때 사용하는 클래스로 폼 컨트롤에 이름이 없고 폼 컨트롤 개수가 변한다면 FormGroup 대신 FormArray를 사용하면 좋다. FormGroup 인스턴스와 마찬가지로 FormArray도 폼 컨트롤을 동적으로 추가하거나 제거할 수 있으며, 자식 FormGroup을 모아 값이나 유효성 검사 결과를 한 번에 참조할 수도 있다. FormArray는 이름을 지정하는 방식으로 정의하지 않는데, 자식 폼 컨트롤의 개수가 몇개인지 몰라도 된다.

  1. FormArray 클래스를 로드
    import { FormArray } from '@angular/forms';
  2. FormArray 컨트롤을 정의
    profileForm = this.fb.group({
      firstName: ['', Validators.required],
      lastName: [''],
      address: this.fb.group({
        street: [''],
        city: [''],
        state: [''],
        zip: ['']
      }),
      aliases: this.fb.array([
        this.fb.control('')
      ])
    });
  3. get() 메서드를 사용해서 FormArray 컨트롤에 접근
    get aliases() {
      return this.profileForm.get('aliases') as FormArray;
    }
    
    addAlias() {
    this.aliases.push(this.fb.control(''));
    }
  4. 템플릿에 FormArray를 연결
    <div formArrayName="aliases">
      <h2>Aliases</h2>
      <button (click)="addAlias()" type="button">+ Add another alias</button>
    
      <div *ngFor="let alias of aliases.controls; let i=index">
        <!-- alias 폼 배열이 반복되는 부분 -->
        <label for="alias-{{ i }}">Alias:</label>
        <input id="alias-{{ i }}" type="text" [formControlName]="i">
      </div>
    </div>

Form Validation

폼 유효성 검사(Form validation) 는 사용자가 필수 항목을 모두 입력했는지, 입력한 내용이 올바른지 검증하는 것이다.

  1. 폼 컴포넌트에 유효성 검사 함수를 로드
    import { Validators } from '@angular/forms';
  2. 원하는 폼 필드에 유효성 검사기를 적용
    profileForm = this.fb.group({
      firstName: ['', Validators.required],
      lastName: [''],
      address: this.fb.group({
        street: [''],
        city: [''],
        state: [''],
        zip: ['']
      }),
    });
  3. 폼 유효성 검사 결과에 따라 적절한 처리 로직을 추가

Form 상태 표시하는 방법

<p>Form Status: {{ profileForm.status }}</p>

validation 종류

min 컨트롤의 값이 제공된 숫자보다 크거나 같은지 유효성 검사
max 컨트롤의 값이 제공된 숫자보다 작거나 같은지 유효성 검사
required 값이 비어있지 않은지 유효성 검사
requiredTrue 값이 참인지 유효성 검사(필수 체크박스)
email 이메일 유효성 검사
minLength 컨트롤의 값이 제시한 최소 길이보다 크거나 같은지 유효성 검사
maxLength 컨트롤의 값이 제시한 최대 길이보다 작거나 같은지 유효성 검사
pattern 정규식 패턴과 일치하는지 유효성 검사
nullValidator 작업을 수행하지 않는 유효성 검사
compose 제공된 컨트롤에 대한 개별 오류 맵의 합집합을 반환하는 단일 함수로 여러 유효성 검사
composeAsync 여러 비동기 유효성 검사기를 제공된 컨트롤에 대한 개별 오류 개체의 합집합을 반환
class Validators {
  static min(min: number): ValidatorFn
  static max(max: number): ValidatorFn
  static required(control: AbstractControl): ValidationErrors | null
  static requiredTrue(control: AbstractControl): ValidationErrors | null
  static email(control: AbstractControl): ValidationErrors | null
  static minLength(minLength: number): ValidatorFn
  static maxLength(maxLength: number): ValidatorFn
  static pattern(pattern: string | RegExp): ValidatorFn
  static nullValidator(control: AbstractControl): ValidationErrors | null
  static compose(validators: ValidatorFn[]): ValidatorFn | null
  static composeAsync(validators: AsyncValidatorFn[]): AsyncValidatorFn | null
}
최신글