ABOUT ME

-

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

    결론

    이 페이지는 다음 내용을 다루었습니다.

    l  리액티브폼 컴포넌트 및 해당 템플릿을 작성하는 방법.

    l  FormBuilder를 사용하여 리액티브 폼 코딩을 단순화하는 방법.

    l  FormControls 그룹화.

    l  FormControl 프로퍼티 검사하기.

    l  patchValue setValue를 사용한 데이터 설정

    l  FormArray로 동적으로 그룹추가

    l  FormControl의 값에 대한 변경사항 관찰

    l  폼 변경사항 저장

    최종 버전의 주요 파일은 다음과 같습니다.

    src/app/app.component.ts

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

     

    @Component({

      selector: 'app-root',

      template: `

      <div class="container">

        <h1>Reactive Forms</h1>

        <hero-list></hero-list>

      </div>

      `

    })

    export class AppComponent {}

     

    src/app/app.module.ts

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

    import { BrowserModule }       from '@angular/platform-browser';

    import { ReactiveFormsModule } from '@angular/forms'// <-- #1 import module

     

    import { AppComponent }        from './app.component';

    import { HeroDetailComponent } from './hero-detail.component'; // <-- #1 import component

    import { HeroListComponent }   from './hero-list.component';

     

    import { HeroService }         from './hero.service'; //  <-- #1 import service

     

    @NgModule({

      imports: [

        BrowserModule,

        ReactiveFormsModule // <-- #2 add to @NgModule imports

      ],

      declarations: [

        AppComponent,

        HeroDetailComponent, // <-- #3 declare app component

        HeroListComponent

      ],

      exports: [ // export for the DemoModule

        AppComponent,

        HeroDetailComponent,

        HeroListComponent

      ],

      providers: [ HeroService ], // <-- #4 provide HeroService

      bootstrap: [ AppComponent ]

    })

    export class AppModule { }

     

    src/app/hero-detail.component.ts

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

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

     

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

    import { HeroService }           from './hero.service';

     

    @Component({

      selector: 'hero-detail',

      templateUrl: './hero-detail.component.html'

    })

    export class HeroDetailComponent implements OnChanges {

      @Input() hero: Hero;

     

      heroForm: FormGroup;

      nameChangeLog: string[] = [];

      states = states;

     

      constructor(

        private fb: FormBuilder,

        private heroService: HeroService) {

     

        this.createForm();

        this.logNameChange();

      }

     

      createForm() {

        this.heroForm = this.fb.group({

          name: ['',Validators.required],

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

          power: '',

          sidekick: ''

        });

      }

     

      ngOnChanges() {

        this.heroForm.reset({

          name: this.hero.name

        });

        this.setAddresses(this.hero.addresses);

      }

     

      get secretLairs(): FormArray {

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

      };

     

      setAddresses(addresses: Address[]) {

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

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

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

      }

     

      addLair() {

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

      }

     

      onSubmit() {

        this.hero = this.prepareSaveHero();

        this.heroService.updateHero(this.hero).subscribe(/* error handling */);

        this.ngOnChanges();

      }

     

      prepareSaveHero(): Hero {

        const formModel = this.heroForm.value;

     

        // deep copy of form model lairs

        const secretLairsDeepCopy: Address[] = formModel.secretLairs.map(

          (address: Address) => Object.assign({}, address)

        );

     

        // return new `Hero` object containing a combination of original hero value(s)

        // and deep copies of changed form model values

        const saveHero: Hero = {

          id: this.hero.id,

          name: formModel.name as string,

          // addresses: formModel.secretLairs // <-- bad!

          addresses: secretLairsDeepCopy

        };

        return saveHero;

      }

     

      revert() { this.ngOnChanges(); }

     

      logNameChange() {

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

        nameControl.valueChanges.forEach(

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

        );

      }

    }

     

    src/app/hero-detail.component.html

    <form [formGroup]="heroForm" (ngSubmit)="onSubmit()" novalidate>

      <div style="margin-bottom: 1em">

        <button type="submit"

            [disabled]="heroForm.pristine" class="btn btn-success">Save</button> &nbsp;

        <button type="reset" (click)="revert()"

            [disabled]="heroForm.pristine" class="btn btn-danger">Revert</button>

      </div>

     

      <!-- Hero Detail Controls -->

      <div class="form-group">

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

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

        </label>

      </div>

     

      <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>

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

      </div>

      <div class="form-group radio">

        <h4>Super power:</h4>

        <label class="center-block"><input type="radio" formControlName="power" value="flight">Flight</label>

        <label class="center-block"><input type="radio" formControlName="power" value="x-ray vision">X-ray vision</label>

        <label class="center-block"><input type="radio" formControlName="power" value="strength">Strength</label>

      </div>

      <div class="checkbox">

        <label class="center-block">

        <input type="checkbox" formControlName="sidekick">I have a sidekick.

        </label>

      </div>

      </form>

     

      <p>heroForm value: {{ heroForm.value | json}}</p>

     

      <h4>Name change log</h4>

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

     

    src/app/hero-list.component.html

    <h3 *ngIf="isLoading"><i>Loading heroes ... </i></h3>

    <h3 *ngIf="!isLoading">Select a hero:</h3>

     

    <nav>

      <button (click)="getHeroes()" class="btn btn-primary">Refresh</button>

      <a *ngFor="let hero of heroes | async" (click)="select(hero)">{{hero.name}}</a>

    </nav>

     

    <div *ngIf="selectedHero">

      <hr>

      <h2>Hero Detail</h2>

      <h3>Editing: {{selectedHero.name}}</h3>

      <hero-detail [hero]="selectedHero"></hero-detail>

    </div>

     

    src/app/hero-list.component.ts

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

    import { Observable }        from 'rxjs/Observable';

    import 'rxjs/add/operator/finally';

     

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

    import { HeroService } from './hero.service';

     

    @Component({

      selector: 'hero-list',

      templateUrl: './hero-list.component.html'

    })

    export class HeroListComponent implements OnInit {

      heroes: Observable<Hero[]>;

      isLoading = false;

      selectedHero: Hero;

     

      constructor(private heroService: HeroService) { }

     

      ngOnInit() { this.getHeroes(); }

     

      getHeroes() {

        this.isLoading = true;

        this.heroes = this.heroService.getHeroes()

                          // Todo: error handling

                          .finally(() => this.isLoading = false);

        this.selectedHero = undefined;

      }

     

      select(hero: Hero) { this.selectedHero = hero; }

    }

     

    src/app/data-model.ts

    export class Hero {

      id = 0;

      name = '';

      addresses: Address[];

      }

     

      export class Address {

      street = '';

      city   = '';

      state  = '';

      zip    = '';

      }

     

      export const heroes: Hero[] = [

      {

        id: 1,

        name: 'Whirlwind',

        addresses: [

        {street: '123 Main',  city: 'Anywhere', state: 'CA',  zip: '94801'},

        {street: '456 Maple', city: 'Somewhere', state: 'VA', zip: '23226'},

        ]

      },

      {

        id: 2,

        name: 'Bombastic',

        addresses: [

        {street: '789 Elm',  city: 'Smallville', state: 'OH',  zip: '04501'},

        ]

      },

      {

        id: 3,

        name: 'Magneta',

        addresses: [ ]

      },

      ];

     

      export const states = ['CA', 'MD', 'OH', 'VA'];

     

    src/app/hero.service.ts

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

     

    import { Observable } from 'rxjs/Observable';

    import { of }         from 'rxjs/observable/of';

    import 'rxjs/add/operator/delay';

     

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

     

    @Injectable()

    export class HeroService {

     

      delayMs = 500;

     

      // Fake server get; assume nothing can go wrong

      getHeroes(): Observable<Hero[]> {

        return of(heroes).delay(this.delayMs); // simulate latency with delay

      }

     

      // Fake server update; assume nothing can go wrong

      updateHero(hero: Hero): Observable<Hero>  {

        const oldHero = heroes.find(h => h.id === hero.id);

        const newHero = Object.assign(oldHero, hero); // Demo: mutate cached hero

        return of(newHero).delay(this.delayMs); // simulate latency with delay

      }

    }

    이 가이드의 모든 단계에 대한 전체 소스는 Reactive Forms Demo / 다운로드 예제 라이브 예제에서 다운로드할 수 있습니다.

     

    댓글

Designed by Tistory.