项目结构

一 首页 ( 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[] = ['山东', '江苏', '福建', '四川'];

Angular之响应式表单 ( Reactive Forms )的更多相关文章

  1. Angular Reactive Forms -- Model-Driven Forms响应式表单

    Angular 4.x 中有两种表单: Template-Driven Forms - 模板驱动式表单 (类似于 AngularJS 1.x 中的表单 )  官方文档:https://v2.angul ...

  2. ng2响应式表单-翻译与概括官网REACTIVE FORMS页面

    本文将半翻译半总结的讲讲ng2官网的另一个未翻译高级教程页面. 原文地址. 文章目的是使用ng2提供的响应式表单技术快速搭出功能完善丰富的界面表单组件. 响应式表单是一项响应式风格的ng2技术,本文将 ...

  3. Angular2响应式表单-翻译与概括官网REACTIVE FORMS页面

    本文将半翻译半总结的讲讲ng2官网的另一个未翻译高级教程页面. 原文地址. 文章目的是使用ng2提供的响应式表单技术快速搭出功能完善丰富的界面表单组件. 响应式表单是一项响应式风格的ng2技术,本文将 ...

  4. Angular2响应式表单

    本文将半翻译半总结的讲讲ng2官网的另一个未翻译高级教程页面. 原文地址. 文章目的是使用ng2提供的响应式表单技术快速搭出功能完善丰富的界面表单组件. 响应式表单是一项响应式风格的ng2技术,本文将 ...

  5. Angular11 模板表单、响应式表单(自定义验证器)、HTTP、表单元素双向绑定

    1 模板表单 模型通过指令隐式创建 技巧01:需要在模块级别引入 FormsModule ,通常在共享模块中引入再导出,然后在需要用到 FormsModule 的模块中导入共享模块就可以啦 impor ...

  6. angular6的响应式表单

    1:在AppModule模块里面引入 ReactiveFormsModule 要使用响应式表单,就要从@angular/forms包中导入ReactiveFormsModule,并把它添加到你的NgM ...

  7. angular响应式表单 - 状态字段

    用于表单验证的过程: touched,untoched pristine,dirty pending

  8. angular 响应式表单(登录实例)

    一.表单验证 1. 只有一个验证规则: this.myGroup = this.fb.group({ email:['hurong.cen@qq.com',Validators.required], ...

  9. 【译】用 Chart.js 做漂亮的响应式表单

    数据包围着我们.虽然搜索引擎和其他应用都对基于文本方式表示的数据偏爱有加,但人们发现可视化是更容易理解的一种方式.今年初,SitePoint 发表了 Aurelio 的文章< Chart.js简 ...

随机推荐

  1. 排查bug的步骤

    原创文章,欢迎阅读,禁止转载. bug预防C/C++代码发布前的检查:检查有没有低级错误,可用cppcheck (bug预防是指在写程序的时候,bug没出现,积极的进行预防,减少.包括良好的编码风格. ...

  2. linux 源码编译php的参数

    ./configure --prefix=/usr/local/php-5.3.5 --with-config-file-path=/usr/local/php-5.3.5/etc --with-co ...

  3. Centos下lnmp正确iptables配置规则

    查看iptable运行状态 service iptables status 清除已有规则 iptables -Fiptables -Xiptables -Z 开放端口 #允许本地回环接口(即运行本机访 ...

  4. php计算程序运行时间

    这里介绍一下 microtime() 这个函数,microtime() 用的不多,但是不能不知道这个函数,它是返回当前 Unix 时间戳和微秒数.例如:echo microtime(); 会返回:0. ...

  5. Ajax技术剖析

    Ajax的全称是Asynchronous JavaScript and XML,是JS的特有功能,它作用是异步JS数据交互,即在不进行页面刷新的情况下进行部分数据的获取,性能较高.值得注意的是,仅有A ...

  6. OC代码编译成c++代码 编译器命令

    xcrun -sdk iphoneos clang -arch x86_64 -rewrite-objc Person+Test.m clang -rewrite-objc -fobjc-arc -s ...

  7. 正则前面的 (?i) (?s) (?m) (?is) (?im)

    (?i) 表示所在位置右侧的表达式开启忽略大小写模式(?s) 表示所在位置右侧的表达式开启单行模式(?m) 表示所在位置右侧的表示式开启指定多行模式(?is) 更改句点字符 (.) 的含义,以使它与每 ...

  8. spark 集成elasticsearch

    pyspark读写elasticsearch依赖elasticsearch-hadoop包,需要首先在这里下载,版本号可以通过自行修改url解决. """ write d ...

  9. spring boot 中使用拦截器

    第一步: 第二步:

  10. 吴裕雄 python 数据处理(3)

    import time a = time.time()print(a)b = time.localtime()print(b)c = time.strftime("%Y-%m-%d %X&q ...