项目结构

一 首页 ( 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. node 支持es6

    安装  babel-cli, 全局安装 npm install babel-cli -g 然后 在工程目录下 安装 npm install babel-cli --save npm install b ...

  2. Dubbo 消费者

    1. pom <dependency> <groupId>com.alibaba</groupId> <artifactId>dubbo</art ...

  3. Spring Cloud (3)B Ribbon 负载均衡 IRule

    package com.service.config; import com.netflix.loadbalancer.IRule;import com.netflix.loadbalancer.Ra ...

  4. mp3-

  5. Python 百分号字符串拼接

    # %s可以接收一切 %d只能接收数字 msg = 'i am %s my hobby is %s' %('lhf','alex') print msg msg2 = 'i am %s my hobb ...

  6. UnicodeDecodeError: 'ascii' codec can't decode byte 0x9c in position 1: ordinal not in range(128)

    待研究: compressed_data = zlib.compress(json.dumps(data), 9) file_data = MySQLdb.escape_string(compress ...

  7. MySQL大数据量分页查询方法及其优化

    MySQL大数据量分页查询方法及其优化   ---方法1: 直接使用数据库提供的SQL语句---语句样式: MySQL中,可用如下方法: SELECT * FROM 表名称 LIMIT M,N---适 ...

  8. 1.Python基础知识小结:

    Python3下载地址:https://www.python.org/downloads/windows/ python3 windows安装参考地址: https://jingyan.baidu.c ...

  9. 基于Delphi的接口编程入门

    为什么使用接口? 举个例子好了:有这样一个卖票服务,电影院可以卖票,歌剧院可以卖票,客运站也可以卖票,那么我们是否需要把电影院..歌剧院和客运站都设计成一个类架构以提供卖票服务?要知道,连经理人都可以 ...

  10. Open Tools API :IDE Main Menus

    http://www.davidghoyle.co.uk/WordPress/?p=777 http://www.davidghoyle.co.uk/WordPress/?page_id=1110 h ...