ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 04 리액티브 폼(Reactive Forms) 12
    앵귤러/04 폼(Forms) 2017. 10. 22. 21:16

    FormArray를 사용하여 FormGroup 배열 표시

    지금까지 FormControls FormGroup을 배웠습니다. FormGroup은 프로퍼티 값이 FormControls 및 다른 FormGroup 으로 이루어진 명명된 객체입니다.

    때로는 임의의 수의 컨트롤이나 그룹을 제시할 필요가 있습니다. 예를 들어, 영웅은 0, 1 또는 임의의 수의 주소를 가질수 있습니다.

    Hero.addresses프로퍼티는 Address 인스턴스의 배열입니다. 주소 FormGroup은 주소 하나를 표시할 수 있습니다. Angular FormArray는 주소 FormGroups의 배열을 표시 할 수 있습니다.

    FormArray 클래스에 액세스하려면 hero-detail.component.ts로 가져옵니다.

    src/app/hero-detail.component.ts (excerpt)

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

    import { FormArray, FormBuilder, FormGroup } from '@angular/forms';

     

    import { Address, Hero, states } from './data-model';

    FormArray로 작업하기 위하여 다음을 수행합니다.

    1.      배열에 항목 (FormControls 또는 FormGroups)을 정의합니다.

    2.      데이터 모델의 데이터로 생성된 항목으로 배열을 초기화합니다.

    3.      사용자가 요구에 따라 항목을 추가하고 제거합니다.

    이 가이드에서는 Hero.address에 대한 FormArray를 정의하고 사용자가 주소를 추가하거나 수정하도록 합니다 (주소 제거는 숙제임).

    HeroDetailComponent 생성자에서 폼모델을 다시 정의해야 합니다. 현재 이 생성자는 주소FormGroup의 첫번째 영웅 주소만 표시합니다.

    src/app/hero-detail-7.component.ts

    this.heroForm = this.fb.group({

          name: ['',Validators.required],

          address: this.fb.group(new Address()), // <-- a FormGroup with a new address

          power: '',

          sidekick: ''

        });

     

    주소를 은신처로

    사용자의 관점에서 영웅에게는 주소가 없습니다. 주소는 일반인을 위한 것입니다. 영웅은 은신처를 가지고 있습니다. 주소 FormGroup 정의를 은신처 FormArray 정의로 바꿉니다.

    src/app/hero-detail-8.component.ts

    this.heroForm = this.fb.group({

          name: ['',Validators.required],

          secretLairs: this.fb.group([]), // <--secretLairs as an empty FormArray

          power: '',

          sidekick: ''

        });

     

    폼컨트롤 이름을 address에서 secretLairs로 변경한다는 것은 폼모델이 데이터 모델과 일치 할 필요가 없다는 사실을 보여줍니다.

    분명히 둘 사이에 관계가 있어야합니다. 그러나 응용프로그램 도메인 내에서 의미있는 것이 될 수 있습니다.

    프레젠테이션 요구사항은 종종 데이터 요구사항과 다릅니다. 리액티브폼 접근 방식은 이 구분을 강조하고 촉진합니다.

     

    "secretLairs"FormArray 초기화

    디폴트 폼은 주소도 없고, 이름도 없는 영웅을 표시합니다.

    부모 HeroListComponent HeroDetailComponent.hero 입력 프로퍼티를 새로운 Hero로 설정할 때마다 secretLair에 실제 영웅 주소를 채우는 (또는 다시 채우는) 방법이 필요합니다.

    다음 setAddresses 메서드는 secretLair FormArray를 영웅 주소 FormGroups 배열로 초기화 된 새 FormArray로 바꿉니다.

    src/app/hero-detail-8.component.ts

    setAddresses(addresses: Address[]) {

        const addressFGs = addresses.map(address => this.fb.group(address));

        const addressFormArray = this.fb.array(addressFGs);

        this.heroForm.setControl('secretLairs', addressFormArray);

      }

    이전 FormArray setValue가 아닌 FormGroup.setControl 메서드로 바꿉니다. 당신은 컨트롤의 값이 아니라 컨트롤을 대체하고 있습니다.

    SecretLairs FormArray Addresses가 아닌 FormGroups 포함하고 있습니다.

     

    FormArray 가져 오기

    HeroDetailComponent secretLairs FormArray에서 항목을 표시, 추가 및 삭제할 수 있어야합니다.

    FormGroup.get 메서드를 사용하여 해당 FormArray에 대한 참조를 가져옵니다. 명확함과 재사용을 위해 secretLairs 편의 프로퍼티에 표현식을 감쌉니다.

    src/app/hero-detail.component.ts (secretLayers property)

    get secretLairs(): FormArray {

        return this.heroForm.get('secretLairs') as FormArray;

      };

     

    FormArray 표시

    현재 HTML 템플리트는 단일 주소 FormGroup을 표시합니다. 영웅의 주소 FormGroups 0 , 1 개 또는 그 이상의 주소를 표시하도록 수정하십시오.

    이는 주로 <div>의 주소에 대한 이전 템플릿 HTML을 랩핑하고 *ngFor로 해당 <div>를 반복하는 것입니다.

    비결은 *ngFor를 작성하는 방법을 아는 데 있습니다. 다음 세 가지 핵심 사항이 있습니다.

    1.      <div> *ngFor와 함께 다른 래핑 <div>을 추가하고 formArrayName 디렉티브를 "secretLairs"로 설정하십시오. 이 단계에서는 내부 HTML 서식 파일의 폼컨트롤 컨텍스트로 secretLairs FormArray를 설정합니다.

    2.      반복되는 항목의 소스는 FormArray 자체가 아닌 FormArray.controls입니다. 각 컨트롤은 이전에 (지금 반복 된) 템플릿 HTML이 예상했던 것과 정확히 일치하는 주소 FormGroup입니다.

    3.      FormGroup을 반복할 때마다 고유한 formGroupName이 필요합니다. formGroupName FormArray에서 FormGroup의 인덱스 여야합니다. 해당 색인을 다시 사용하여 각 주소에 대한 고유한 레이블을 작성합니다.

    다음은 HTML 템플릿의 비밀 은신처 섹션의 골격입니다.

    src/app/hero-detail.component.html (*ngFor)

      <div formArrayName="secretLairs" class="well well-lg">

        <div *ngFor="let address of secretLairs.controls; let i=index" [formGroupName]="i" >

        <!-- The repeated address template -->

        </div>

      </div>

    다음은 비밀 은신처 섹션의 전체 템플릿입니다.

    src/app/hero-detail.component.html (excerpt)

      <div formArrayName="secretLairs" class="well well-lg">

        <div *ngFor="let address of secretLairs.controls; let i=index" [formGroupName]="i" >

        <!-- The repeated address template -->

        <h4>Address #{{i + 1}}</h4>

        <div style="margin-left: 1em;">

          <div class="form-group">

          <label class="center-block">Street:

            <input class="form-control" formControlName="street">

          </label>

          </div>

          <div class="form-group">

          <label class="center-block">City:

            <input class="form-control" formControlName="city">

          </label>

          </div>

          <div class="form-group">

          <label class="center-block">State:

            <select class="form-control" formControlName="state">

            <option *ngFor="let state of states" [value]="state">{{state}}</option>

            </select>

          </label>

          </div>

          <div class="form-group">

          <label class="center-block">Zip Code:

            <input class="form-control" formControlName="zip">

          </label>

          </div>

        </div>

        <br>

        <!-- End of the repeated address template -->

        </div>

      </div>

     

    FormArray에 새 은신처 추가

    secretLairs FormArray를 가져오고 FormGroup에 새 주소 FormGroup을 추가하는 addLair 메서드를 만듭니다.

    src/app/hero-detail.component.ts (addLair method)

    addLair() {

        this.secretLairs.push(this.fb.group(new Address()));

      }

    폼에 버튼을 배치하여 사용자가 새 비밀 은신처를 추가하고 이를 컴포넌트의 addLair 메서드에 연결할 수 있습니다.

    src/app/hero-detail.component.html (addLair button)

        <button (click)="addLair()" type="button">Add a Secret Lair</button>

     

    type = "button"어트리뷰트를 추가하십시오. 사실, 버튼 타입을 항상 지정해야 합니다. 명시적인 타입이 없으면 버튼 타입의 기본값은 "submit"입니다. 나중에 폼 제출 액션을 추가할 때마다 모든 "제출"버튼이 submit 액션를 트리거하여 현재 변경 사항 저장과 같은 조치를 취할 수 있습니다. 사용자가 Add a Secret Lair 버튼을 클릭할 때 변경 사항을 저장하는 것을 원치않을 것입니다..

    시도!

    브라우저에서 "Magneta"라는 영웅을 선택하십시오. "Magneta"에는 주소가 없으므로 양식 맨 아래의 진단 JSON에서 볼 수 있습니다.


    "Add a Secret Lair"버튼을 클릭하십시오. 새 주소 섹션이 나타납니다.

     

    은신처 제거

    이 예제는 주소를 추가할 수 있지만 제거할 수는 없습니다. 숙제로 남겨 놓았으므로 removeLair 메소드를 작성하고 반복되는 주소 HTML에 버튼에 연결하십시오.

     

    컨트롤 변경 관찰

    Angular는 사용자가 부모 HeroListComponent에서 영웅을 선택할 때 ngOnChanges를 호출합니다. 영웅을 선택하면 HeroDetailComponent.hero 입력 프로퍼티가 변경됩니다.

    Angular는 사용자가 영웅의 이름이나 비밀 은신처를 수정할 때 ngOnChanges를 호출하지 않습니다. 다행히도 변경 이벤트를 발생시키는 폼 컨트롤 프로퍼티 중 하나를 구독함으로써 이러한 변경 사항을 알 수 있습니다.

    RxJS Observable을 반환하는 valueChanges와 같은 프로퍼티입니다. 폼컨트롤 값을 모니터링하기 위해 RxJS Observable에 대해 많이 알 필요는 없습니다.

    다음 메서드를 추가하여 FormControl이라는 이름 값의 변경 내용을 기록합니다.

    src/app/hero-detail.component.ts (logNameChange)

    nameChangeLog: string[] = [];

    logNameChange() {

        const nameControl = this.heroForm.get('name');

        nameControl.valueChanges.forEach(

          (value: string) => this.nameChangeLog.push(value)

        );

      }

    폼을 생성한 후 생성자에서 호출합니다.

    src/app/hero-detail-8.component.ts

    constructor(private fb: FormBuilder) {

        this.createForm();

        this.logNameChange();

      }

    logNameChange 메소드는 이름 변경 값을 nameChangeLog 배열로 푸시합니다. *ngFor 바인딩을 사용하여 컴포넌트 템플릿의 맨 아래에 이 배열을 표시하십시오.

    src/app/hero-detail.component.html (Name change log)

    <h4>Name change log</h4>

      <div *ngFor="let name of nameChangeLog">{{name}}</div>

    브라우저로 돌아가 영웅 ( : '마그네 타')을 선택하고 이름 입력 상자에 입력을 시작합니다. 각 키 입력 후 로그에 새 이름이 표시되어야합니다.

     

    사용시기

    보간 바인딩은 이름 변경을 표시하는 좀더 쉬운 방법입니다. 옵저버블 폼컨트롤 프로퍼티를 구독하면 컴포넌트 클래스 내에서 응용프로그램 로직을 트리거하는 데 편리합니다.

    댓글

Designed by Tistory.