• Angular之响应式表单 ( Reactive Forms )


    项目结构

    一 首页 ( index.html )

    <!doctype html>
    <html lang="en">
    <head>
      <meta charset="utf-8">
      <title>Angular4ReactiveForm</title>
      <base href="/">
    
      <meta name="viewport" content="width=device-width, initial-scale=1">
      <link rel="icon" type="image/x-icon" href="favicon.ico">
    </head>
    <body>
      <app-hero-list></app-hero-list>
    </body>
    </html>

    二 根模块 ( app.module.ts )

    import { BrowserModule } from '@angular/platform-browser';
    import { ReactiveFormsModule } from '@angular/forms';
    import { NgModule } from '@angular/core';
    
    
    import { HeroListComponent } from './hero-list/hero-list.component';
    import { HeroDetailComponent } from './hero-detail/hero-detail.component';
    import { HeroService } from './hero.service';
    
    
    @NgModule({
      declarations: [
        HeroListComponent,
        HeroDetailComponent
      ],
      imports: [
        BrowserModule,
        ReactiveFormsModule
      ],
      providers: [HeroService],
      bootstrap: [HeroListComponent]
    })
    export class AppModule { }

    三 列表脚本 ( hero-list.component.ts )

    import { Component, OnInit } from '@angular/core';
    import { Observable } from 'rxjs/Observable';
    import { finalize } from 'rxjs/operators';
    import { Hero } from '../model/model';
    import { HeroService } from '../hero.service';
    
    @Component({
      selector: 'app-hero-list',
      templateUrl: './hero-list.component.html',
      styleUrls: ['./hero-list.component.css']
    })
    export class HeroListComponent implements OnInit {
      isLoading = false;
      heroes: Observable<Hero[]>;
      selectedHero: Hero;
      constructor(public heroService: HeroService) { }
    
      ngOnInit() {
      }
    
      /**
       * 获取Hero列表
       * 
       * @memberof HeroListComponent
       */
      getHeroes() {
        this.isLoading = true;
        this.heroes = this.heroService.getHeroes()
          .pipe(finalize(() => this.isLoading = false));
        this.selectedHero = null;
      }
    
      /**
       * 选择Hero
       * 
       * @param {Hero} hero 
       * @memberof HeroListComponent
       */
      select(hero: Hero) {
        this.selectedHero = hero;
      }
    
    }

    四 列表模版 ( hero-list.component.html )

    <h3 *ngIf="isLoading">
      <i>Loading heroes ... </i>
    </h3>
    <h3 *ngIf="!isLoading">
      <i>Select a hero</i>
    </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>
      <app-hero-detail [hero]="selectedHero"></app-hero-detail>
    </div>

    五 详情脚本 ( hero-detail.component.ts )

    import { Component, OnInit, Input, OnChanges, OnDestroy } from '@angular/core';
    import { Hero, Address } from '../model/model';
    import { FormBuilder, FormGroup, FormArray, AbstractControl, FormControl } from '@angular/forms';
    import { HeroService } from '../hero.service';
    import { provinces } from '../model/model';
    
    @Component({
      selector: 'app-hero-detail',
      templateUrl: './hero-detail.component.html',
      styleUrls: ['./hero-detail.component.css']
    })
    export class HeroDetailComponent implements OnInit, OnChanges, OnDestroy {
      @Input() hero: Hero;
      heroForm: FormGroup;
      provinces: string[] = provinces;
      nameChangeLog: string[] = [];
      constructor(private fb: FormBuilder, private heroService: HeroService) {
        this.createForm();
        this.logNameChanges();
      }
    
      /**
       * 
       * getter方法:从而可以直接访问secretLairs
       * @readonly
       * @type {FormArray}
       * @memberof HeroDetailComponent
       */
      get secretLairs(): FormArray {
        return <FormArray>this.heroForm.get('secretLairs');
      }
    
      ngOnInit() { // 单击Hero按钮,选择Hero时执行
        console.log('详情页面初始化');
      }
    
      ngOnDestroy(): void { // 单击Refresh按钮,重新获取Hero列表时执行
        console.log('详情页面销毁');
      }
    
      ngOnChanges() {
        this.rebuildForm();
      }
    
      createForm() {
        this.heroForm = this.fb.group({
          name: '',
          secretLairs: this.fb.array([]),
          power: '',
          sidekick: ''
        });
      }
    
      /**
       *
       * 选择英雄、还原表单时重置表单
       * @memberof HeroDetailComponent
       */
      rebuildForm() {
        this.heroForm.reset({ // 将字段标记为pristine、untouched
          name: this.hero.name
        });
        this.setAddress(this.hero.addresses);
      }
    
      /**
       *
       * 设置表单的地址
       * @param {Address[]} addresses
       * @memberof HeroDetailComponent
      */
      setAddress(addresses: Address[]) {
        const addressFormGroups = addresses.map(address => this.fb.group(address));
        const addressForArray = this.fb.array(addressFormGroups);
        this.heroForm.setControl('secretLairs', addressForArray);
      }
      /**
       * 新增一个地址
       * 
       * @memberof HeroDetailComponent
       */
      addLair() {
        this.secretLairs.push(this.fb.group(new Address()));
      }
    
      /**
       * 保存表单
       * 
       * @memberof HeroDetailComponent
       */
      save() {
        this.hero = this.prepareCopyHero();
        this.heroService.updateHero(this.hero).subscribe(
          (val) => { // 成功
    
          },
          (err) => { // 出错
    
          });
        this.rebuildForm();
      }
    
      /**
       * 深度复制Hero对象
       * 
       * @returns {Hero} 
       * @memberof HeroDetailComponent
       */
      prepareCopyHero(): Hero {
        const formModel: any = this.heroForm.value; // AbstractControl是FormGroup、FormArray、FormControl的基类
        const secrectLairDeepCopy: Address[] = formModel.secretLairs.map(
          (address: Address) => Object.assign({}, address)
        );
        const savedHero: Hero = {
          id: this.hero.id,
          name: formModel.name,
          addresses: secrectLairDeepCopy
        };
        return savedHero;
      }
    
      /**
       * 还原表单
       * 
       * @memberof HeroDetailComponent
       */
      revert() {
        this.rebuildForm();
      }
    
      /**
       * 订阅valueChanges属性( Observale对象 ),监控详情页面名称的变化,选择英雄、输入名称时执行
       * 
       * @memberof HeroDetailComponent
       */
      logNameChanges() {
        const nameControl: FormControl = <FormControl>this.heroForm.get('name');
        nameControl.valueChanges.forEach((val: string) => this.nameChangeLog.push(val));
      }
    }

    六 详情模版 ( hero-detail.component.html )

    <form [formGroup]='heroForm'>
      <!-- 按钮 -->
      <div style="margin-bottom: 1em;">
        <button type="button" (click)="save();" [disabled]="heroForm.pristine" class="btn btn-success">Save</button>
        <button type="button" (click)="revert();" [disabled]="heroForm.pristine" class="btn btn-success">Revert</button>
      </div>
      <!-- 名称 -->
      <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">
          <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">Province:
                <select class="form-control" formControlName="province">
                  <option *ngFor="let province of provinces" [value]="province">{{province}}</option>
                </select>
              </label>
            </div>
            <div class="form-group">
              <label class="center-block">Zip Code:
                <input class="form-control" formControlName="zip" />
              </label>
            </div>
          </div>
        </div>
        <button (click)="addLair();" type="button">Add a Secret Lair</button>
      </div>
      <!-- 地址循环结束 -->
    </form>
    
    <p>heroForm value: {{heroForm.value | json}}</p>
    
    <h4>Name change log</h4>
    <ul>
      <li *ngFor="let name of nameChangeLog">{{name}}</li>
    </ul>

    七 服务脚本 ( hero.service.ts )

    import { Injectable } from '@angular/core';
    import { of } from 'rxjs/observable/of';
    import { delay } from 'rxjs/operators';
    import { Hero, heroes } from './model/model';
    import { Observable } from 'rxjs/Observable';
    
    @Injectable()
    export class HeroService {
    
      delayMs = 500;
    
      constructor() { }
    
      /**
       * 获取Hero对象列表
       * 
       * @returns {Observable<Hero[]>} 
       * @memberof HeroService
       */
      getHeroes(): Observable<Hero[]> {
        return of(heroes).pipe(delay(this.delayMs));
      }
    
    
      /**
       * 更新Hero对象
       * 
       * @param {Hero} hero 
       * @returns {Observable<Hero>} 
       * @memberof HeroService
       */
      updateHero(hero: Hero): Observable<Hero> {
        const oldHero = heroes.find(h => h.id === hero.id);
        const newHero = Object.assign(oldHero, hero); // 潜复制
        return of(newHero).pipe(delay(this.delayMs));
      }
    }

    八 数据模型 ( model.ts )

    export class Hero {
        constructor(public id: number, public name: string, public addresses: Address[]) {
    
        }
    }
    
    export class Address {
        constructor(public province?: string, public city?: string, public street?: string, public zip?: number) {
    
        }
    }
    export const heroes: Hero[] = [
        new Hero(1, 'Whirlwind', [
            new Address('山东', '青岛', '东海路', 266000),
            new Address('江苏', '苏州', '干将路', 215000)
        ]),
        new Hero(2, 'Bombastic', [
            new Address('福建', '厦门', '环岛路', 361000)
        ]),
        new Hero(3, 'Magneta', [])
    ];
    
    export const provinces: string[] = ['山东', '江苏', '福建', '四川'];
  • 相关阅读:
    我的20130220日 在北京 do{ Web Develop } while(!die)
    关于NVelocity模板引擎初学总结
    C# 23种设计模式汇总
    基于模型的设备故障检测
    图像去噪之光斑去除
    虹膜识别
    封闭曲线拟合
    基于故障树的系统可靠性分析
    图像识别之棋子识别
    时间序列的模式距离
  • 原文地址:https://www.cnblogs.com/sea-breeze/p/8946026.html
Copyright © 2020-2023  润新知