Angular之响应式表单 ( Reactive Forms )

Stella981
• 阅读 613

项目结构

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[] = ['山东', '江苏', '福建', '四川'];
点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Easter79 Easter79
3年前
swap空间的增减方法
(1)增大swap空间去激活swap交换区:swapoff v /dev/vg00/lvswap扩展交换lv:lvextend L 10G /dev/vg00/lvswap重新生成swap交换区:mkswap /dev/vg00/lvswap激活新生成的交换区:swapon v /dev/vg00/lvswap
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
5个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Wesley13 Wesley13
3年前
Java获得今日零时零分零秒的时间(Date型)
publicDatezeroTime()throwsParseException{    DatetimenewDate();    SimpleDateFormatsimpnewSimpleDateFormat("yyyyMMdd00:00:00");    SimpleDateFormatsimp2newS
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
11个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这