Angular之响应式表单 ( Reactive Forms )

项目结构

一 首页 ( index.html )

1<!doctype html> 2<html lang="en"> 3<head> 4 <meta charset="utf-8"> 5 <title>Angular4ReactiveForm</title> 6 <base href="/"> 7 8 <meta name="viewport" content="width=device-width, initial-scale=1"> 9 <link rel="icon" type="image/x-icon" href="favicon.ico"> 10</head> 11<body> 12 <app-hero-list></app-hero-list> 13</body> 14</html>

二 根模块 ( app.module.ts )

1import { BrowserModule } from '@angular/platform-browser'; 2import { ReactiveFormsModule } from '@angular/forms'; 3import { NgModule } from '@angular/core'; 4 5 6import { HeroListComponent } from './hero-list/hero-list.component'; 7import { HeroDetailComponent } from './hero-detail/hero-detail.component'; 8import { HeroService } from './hero.service'; 9 10 11@NgModule({ 12 declarations: [ 13 HeroListComponent, 14 HeroDetailComponent 15 ], 16 imports: [ 17 BrowserModule, 18 ReactiveFormsModule 19 ], 20 providers: [HeroService], 21 bootstrap: [HeroListComponent] 22}) 23export class AppModule { }

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

1import { Component, OnInit } from '@angular/core'; 2import { Observable } from 'rxjs/Observable'; 3import { finalize } from 'rxjs/operators'; 4import { Hero } from '../model/model'; 5import { HeroService } from '../hero.service'; 6 7@Component({ 8 selector: 'app-hero-list', 9 templateUrl: './hero-list.component.html', 10 styleUrls: ['./hero-list.component.css'] 11}) 12export class HeroListComponent implements OnInit { 13 isLoading = false; 14 heroes: Observable<Hero[]>; 15 selectedHero: Hero; 16 constructor(public heroService: HeroService) { } 17 18 ngOnInit() { 19 } 20 21 /** 22 * 获取Hero列表 23 * 24 * @memberof HeroListComponent 25 */ 26 getHeroes() { 27 this.isLoading = true; 28 this.heroes = this.heroService.getHeroes() 29 .pipe(finalize(() => this.isLoading = false)); 30 this.selectedHero = null; 31 } 32 33 /** 34 * 选择Hero 35 * 36 * @param {Hero} hero 37 * @memberof HeroListComponent 38 */ 39 select(hero: Hero) { 40 this.selectedHero = hero; 41 } 42 43}

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

1<h3 *ngIf="isLoading"> 2 <i>Loading heroes ... </i> 3</h3> 4<h3 *ngIf="!isLoading"> 5 <i>Select a hero</i> 6</h3> 7<nav> 8 <button (click)="getHeroes();" class="btn btn-primary">Refresh</button> 9 <a *ngFor="let hero of heroes | async" (click)="select(hero);">{{hero.name}}</a> 10</nav> 11<div *ngIf="selectedHero"> 12 <hr/> 13 <h2>Hero Detail</h2> 14 <h3>Editing:{{selectedHero.name}}</h3> 15 <app-hero-detail [hero]="selectedHero"></app-hero-detail> 16</div>

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

1import { Component, OnInit, Input, OnChanges, OnDestroy } from '@angular/core'; 2import { Hero, Address } from '../model/model'; 3import { FormBuilder, FormGroup, FormArray, AbstractControl, FormControl } from '@angular/forms'; 4import { HeroService } from '../hero.service'; 5import { provinces } from '../model/model'; 6 7@Component({ 8 selector: 'app-hero-detail', 9 templateUrl: './hero-detail.component.html', 10 styleUrls: ['./hero-detail.component.css'] 11}) 12export class HeroDetailComponent implements OnInit, OnChanges, OnDestroy { 13 @Input() hero: Hero; 14 heroForm: FormGroup; 15 provinces: string[] = provinces; 16 nameChangeLog: string[] = []; 17 constructor(private fb: FormBuilder, private heroService: HeroService) { 18 this.createForm(); 19 this.logNameChanges(); 20 } 21 22 /** 23 * 24 * getter方法:从而可以直接访问secretLairs 25 * @readonly 26 * @type {FormArray} 27 * @memberof HeroDetailComponent 28 */ 29 get secretLairs(): FormArray { 30 return <FormArray>this.heroForm.get('secretLairs'); 31 } 32 33 ngOnInit() { // 单击Hero按钮,选择Hero时执行 34 console.log('详情页面初始化'); 35 } 36 37 ngOnDestroy(): void { // 单击Refresh按钮,重新获取Hero列表时执行 38 console.log('详情页面销毁'); 39 } 40 41 ngOnChanges() { 42 this.rebuildForm(); 43 } 44 45 createForm() { 46 this.heroForm = this.fb.group({ 47 name: '', 48 secretLairs: this.fb.array([]), 49 power: '', 50 sidekick: '' 51 }); 52 } 53 54 /** 55 * 56 * 选择英雄、还原表单时重置表单 57 * @memberof HeroDetailComponent 58 */ 59 rebuildForm() { 60 this.heroForm.reset({ // 将字段标记为pristine、untouched 61 name: this.hero.name 62 }); 63 this.setAddress(this.hero.addresses); 64 } 65 66 /** 67 * 68 * 设置表单的地址 69 * @param {Address[]} addresses 70 * @memberof HeroDetailComponent 71 */ 72 setAddress(addresses: Address[]) { 73 const addressFormGroups = addresses.map(address => this.fb.group(address)); 74 const addressForArray = this.fb.array(addressFormGroups); 75 this.heroForm.setControl('secretLairs', addressForArray); 76 } 77 /** 78 * 新增一个地址 79 * 80 * @memberof HeroDetailComponent 81 */ 82 addLair() { 83 this.secretLairs.push(this.fb.group(new Address())); 84 } 85 86 /** 87 * 保存表单 88 * 89 * @memberof HeroDetailComponent 90 */ 91 save() { 92 this.hero = this.prepareCopyHero(); 93 this.heroService.updateHero(this.hero).subscribe( 94 (val) => { // 成功 95 96 }, 97 (err) => { // 出错 98 99 }); 100 this.rebuildForm(); 101 } 102 103 /** 104 * 深度复制Hero对象 105 * 106 * @returns {Hero} 107 * @memberof HeroDetailComponent 108 */ 109 prepareCopyHero(): Hero { 110 const formModel: any = this.heroForm.value; // AbstractControl是FormGroup、FormArray、FormControl的基类 111 const secrectLairDeepCopy: Address[] = formModel.secretLairs.map( 112 (address: Address) => Object.assign({}, address) 113 ); 114 const savedHero: Hero = { 115 id: this.hero.id, 116 name: formModel.name, 117 addresses: secrectLairDeepCopy 118 }; 119 return savedHero; 120 } 121 122 /** 123 * 还原表单 124 * 125 * @memberof HeroDetailComponent 126 */ 127 revert() { 128 this.rebuildForm(); 129 } 130 131 /** 132 * 订阅valueChanges属性( Observale对象 ),监控详情页面名称的变化,选择英雄、输入名称时执行 133 * 134 * @memberof HeroDetailComponent 135 */ 136 logNameChanges() { 137 const nameControl: FormControl = <FormControl>this.heroForm.get('name'); 138 nameControl.valueChanges.forEach((val: string) => this.nameChangeLog.push(val)); 139 } 140}

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

1<form [formGroup]='heroForm'> 2 <!-- 按钮 --> 3 <div style="margin-bottom: 1em;"> 4 <button type="button" (click)="save();" [disabled]="heroForm.pristine" class="btn btn-success">Save</button> 5 <button type="button" (click)="revert();" [disabled]="heroForm.pristine" class="btn btn-success">Revert</button> 6 </div> 7 <!-- 名称 --> 8 <div class="form-group"> 9 <label class="center-block">Name: 10 <input class="form-control" formControlName="name" /> 11 </label> 12 </div> 13 <!-- 地址循环开始 --> 14 <div formArrayName="secretLairs" class="well well-lg"> 15 <div *ngFor="let address of secretLairs.controls;let i = index;" [formGroupName]="i"> 16 <h4>Address #{{i+1}}</h4> 17 <div style="margin-left: 1em;"> 18 <div class="form-group"> 19 <label class="center-block">Street: 20 <input class="form-control" formControlName="street" /> 21 </label> 22 </div> 23 <div class="form-group"> 24 <label class="center-block">City: 25 <input class="form-control" formControlName="city" /> 26 </label> 27 </div> 28 <div class="form-group"> 29 <label class="center-block">Province: 30 <select class="form-control" formControlName="province"> 31 <option *ngFor="let province of provinces" [value]="province">{{province}}</option> 32 </select> 33 </label> 34 </div> 35 <div class="form-group"> 36 <label class="center-block">Zip Code: 37 <input class="form-control" formControlName="zip" /> 38 </label> 39 </div> 40 </div> 41 </div> 42 <button (click)="addLair();" type="button">Add a Secret Lair</button> 43 </div> 44 <!-- 地址循环结束 --> 45</form> 46 47<p>heroForm value: {{heroForm.value | json}}</p> 48 49<h4>Name change log</h4> 50<ul> 51 <li *ngFor="let name of nameChangeLog">{{name}}</li> 52</ul>

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

1import { Injectable } from '@angular/core'; 2import { of } from 'rxjs/observable/of'; 3import { delay } from 'rxjs/operators'; 4import { Hero, heroes } from './model/model'; 5import { Observable } from 'rxjs/Observable'; 6 7@Injectable() 8export class HeroService { 9 10 delayMs = 500; 11 12 constructor() { } 13 14 /** 15 * 获取Hero对象列表 16 * 17 * @returns {Observable<Hero[]>} 18 * @memberof HeroService 19 */ 20 getHeroes(): Observable<Hero[]> { 21 return of(heroes).pipe(delay(this.delayMs)); 22 } 23 24 25 /** 26 * 更新Hero对象 27 * 28 * @param {Hero} hero 29 * @returns {Observable<Hero>} 30 * @memberof HeroService 31 */ 32 updateHero(hero: Hero): Observable<Hero> { 33 const oldHero = heroes.find(h => h.id === hero.id); 34 const newHero = Object.assign(oldHero, hero); // 潜复制 35 return of(newHero).pipe(delay(this.delayMs)); 36 } 37}

八 数据模型 ( model.ts )

1export class Hero { 2 constructor(public id: number, public name: string, public addresses: Address[]) { 3 4 } 5} 6 7export class Address { 8 constructor(public province?: string, public city?: string, public street?: string, public zip?: number) { 9 10 } 11} 12export const heroes: Hero[] = [ 13 new Hero(1, 'Whirlwind', [ 14 new Address('山东', '青岛', '东海路', 266000), 15 new Address('江苏', '苏州', '干将路', 215000) 16 ]), 17 new Hero(2, 'Bombastic', [ 18 new Address('福建', '厦门', '环岛路', 361000) 19 ]), 20 new Hero(3, 'Magneta', []) 21]; 22 23export const provinces: string[] = ['山东', '江苏', '福建', '四川'];
点赞
收藏

评论区

加载中...

相关推荐

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

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

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