[Reactive Forms] FormArray 구현을 자식 구성 요소로 잘라냅니다.

11650 단어 AngularTypeScript

마지막으로 한 일



지난번은 아래의 기사에서, 동적으로 추가 & 삭제할 수 있는 폼을 단일의 컴퍼넌트 안에서 구현했습니다.

마지막 기사
[Reactive Forms] FormArray에서 동적으로 추가 삭제할 수 있는 입력 양식 구현

폼을 구현할 때, 복수의 입력란이 필요한 경우가 대부분으로 단일의 컴퍼넌트에 넣으면 가독성이 내려 버립니다. 이번에는 FormArray의 구현 부분을 자식 컴포넌트로 잘라내고 싶습니다.

고민한 곳



여기가 마지막 템플릿입니다.

app.component.html
<h3>フォーム</h3>

<form [formGroup]="form">
  <div>名前: <input type="text" formControlName="userName"></div>

  <div>
    <p>スキル情報</p>
    <div formArrayName="userSkills">
      <div *ngFor="let skill of userSkills.controls; let i = index">
        <div [formGroupName]="i">
          <div>スキル名: <input type="text" formControlName="skillName"></div>
          <div>レベル: <input type="number" formControlName="skillLevel"></div>
          <button (click)="removeUserSkill(i)">削除</button>
        </div>
      </div>
    </div>
    <button (click)="addUserSkills()">スキルを追加</button>
  </div>
</form>

나는 어떤 계층의 부분에서 잘라내면 좋을까? 조금 고민했습니다.
일단의 결론으로서는 FormGroup 를 아이 컴퍼넌트에 건네주어, FormArray 의 부분을 둥글게 잘라내면 잘 구현할 수 있는 것은 아닐까? 라는 결론에 이르렀습니다.

샘플 소스



여기에서는 SkillForm 라는 컴퍼넌트를 새롭게 작성해, 여기에 처리를 잘라 갑니다.

자식 구성 요소 구현



결론부터 쓰면 다음과 같은 컴포넌트의 구현이 됩니다.

skill-form-component.ts
import {Component, Input, OnInit} from '@angular/core';
import {FormArray, FormBuilder, FormGroup} from '@angular/forms';

@Component({
  selector: 'app-skill-form',
  templateUrl: './skill-form.component.html',
  styleUrls: ['./skill-form.component.scss']
})
export class SkillFormComponent implements OnInit {
  @Input() parentForm: FormGroup;

  constructor(private fb: FormBuilder) { }

  ngOnInit() {
  }

  get userSkillForm(): FormGroup {
    return this.fb.group({
      skillName: [''],
      skillLevel: [''],
    });
  }

  get userSkills(): FormArray {
    return this.parentForm.get('userSkills') as FormArray;
  }

  addUserSkills() {
    this.userSkills.push(this.userSkillForm);
  }

  removeUserSkill(index: number) {
    this.userSkills.removeAt(index);
  }
}

거의 그대로 이식하고 있습니다만, 포인트는 @Input() parentForm: FormGroup; 로 부모 컴퍼넌트의 FormGroup 에 바인딩하고 있는 점입니다.

하위 템플릿 구현



skill-form-component.html
<ng-container [formGroup]="parentForm">
  <div>
    <p>スキル情報</p>
    <div formArrayName="userSkills">
      <div *ngFor="let skill of userSkills.controls; let i = index">
        <div [formGroupName]="i">
          <div>スキル名: <input type="text" formControlName="skillName"></div>
          <div>レベル: <input type="number" formControlName="skillLevel"></div>
          <button (click)="removeUserSkill(i)">削除</button>
        </div>
      </div>
    </div>
    <button (click)="addUserSkills()">スキルを追加</button>
  </div>
</ng-container>

FormArray의 기술전에, 부모 컴퍼넌트와 동기한 FormGroup를 정의해 주는 것으로, 예상대로의 거동이 됩니다.

상위 템플릿 구현



app.component.html
  <form [formGroup]="form">
    <div>名前: <input type="text" formControlName="userName"></div>

    <app-skill-form [parentForm]="form"></app-skill-form>

  </form>

부모 템플릿에서 호출 할 때 자식 구성 요소의 FormGroup에 자신의 FormGroup을 전달합니다.

결과적으로 다음과 같이 부모 컴포넌트가 하위 컴포넌트에서 Form 변경을 캡처하고 있음을 알 수 있습니다.



FormGroup이 아니라 FormArray를 바인딩 할 수 없습니까?



할 수 있을지도 모릅니다만, 공식적으로도 기술이 보이지 않았기 때문에 파악할 수 없습니다.
원래 FormArray는 [FormGroup] 가 정의된 가운데, FormArrayName 를 선언하고 이용하기 위해 FormGroup이 있다고 전제일까 추측했습니다.

요약



FormArray에 한정되지 않지만, 아이 컴퍼넌트에 리액티브 폼의 내용을 잘라낼 때에는, 부모의 FormGroup를 바인딩해 주면 잘 구현할 수 있을 것 같습니다.

부모 컴퍼넌트에서는 최종적으로 제출되는 데이터의 취급에 집중할 수 있으므로, 책무를 분리한 구현이 가능합니다.

잘못된 점이 있으면, 지적 부탁드립니다.

참고문헌


  • 리액티브 폼(Angular 한국어 문서)
  • Angular > Reactive Forms의 사용법이 초보자에게 이해하기 어렵기 때문에, 예를 섞어서 구현할 수 있도록 정리해 보았다
  • [Angular] 입력 양식을 추가 삭제할 수 있는 동적 양식을 만들어 봅니다.
  • 좋은 웹페이지 즐겨찾기