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[] = ['山东', '江苏', '福建', '四川'];
Angular之响应式表单 ( Reactive Forms )的更多相关文章
- Angular Reactive Forms -- Model-Driven Forms响应式表单
Angular 4.x 中有两种表单: Template-Driven Forms - 模板驱动式表单 (类似于 AngularJS 1.x 中的表单 ) 官方文档:https://v2.angul ...
- ng2响应式表单-翻译与概括官网REACTIVE FORMS页面
本文将半翻译半总结的讲讲ng2官网的另一个未翻译高级教程页面. 原文地址. 文章目的是使用ng2提供的响应式表单技术快速搭出功能完善丰富的界面表单组件. 响应式表单是一项响应式风格的ng2技术,本文将 ...
- Angular2响应式表单-翻译与概括官网REACTIVE FORMS页面
本文将半翻译半总结的讲讲ng2官网的另一个未翻译高级教程页面. 原文地址. 文章目的是使用ng2提供的响应式表单技术快速搭出功能完善丰富的界面表单组件. 响应式表单是一项响应式风格的ng2技术,本文将 ...
- Angular2响应式表单
本文将半翻译半总结的讲讲ng2官网的另一个未翻译高级教程页面. 原文地址. 文章目的是使用ng2提供的响应式表单技术快速搭出功能完善丰富的界面表单组件. 响应式表单是一项响应式风格的ng2技术,本文将 ...
- Angular11 模板表单、响应式表单(自定义验证器)、HTTP、表单元素双向绑定
1 模板表单 模型通过指令隐式创建 技巧01:需要在模块级别引入 FormsModule ,通常在共享模块中引入再导出,然后在需要用到 FormsModule 的模块中导入共享模块就可以啦 impor ...
- angular6的响应式表单
1:在AppModule模块里面引入 ReactiveFormsModule 要使用响应式表单,就要从@angular/forms包中导入ReactiveFormsModule,并把它添加到你的NgM ...
- angular响应式表单 - 状态字段
用于表单验证的过程: touched,untoched pristine,dirty pending
- angular 响应式表单(登录实例)
一.表单验证 1. 只有一个验证规则: this.myGroup = this.fb.group({ email:['hurong.cen@qq.com',Validators.required], ...
- 【译】用 Chart.js 做漂亮的响应式表单
数据包围着我们.虽然搜索引擎和其他应用都对基于文本方式表示的数据偏爱有加,但人们发现可视化是更容易理解的一种方式.今年初,SitePoint 发表了 Aurelio 的文章< Chart.js简 ...
随机推荐
- 爬虫--Scrapy-CrawlSpider&基于CrawlSpide的分布式爬虫
CrawlSpider 提问:如果想要通过爬虫程序去爬取”糗百“全站数据新闻数据的话,有几种实现方法? 方法一:基于Scrapy框架中的Spider的递归爬取进行实现(Request模块递归回调par ...
- java实现解压zip文件,(亲测可用)!!!!!!
项目结构: Util.java内容: package com.cfets.demo; import java.io.File; import java.io.FileOutputStream; imp ...
- windows下面的java项目打成jar放到XShell终端上面进行远程调试
前言: java项目打成jar放到linux上面运行,但是linux上面没有eclipse不能进行debug,所以要在windows的eclipse中进行远程调试. 需要注意的是!!!-->在e ...
- Delphi处理TWebBrowser的Close事件
当TWebBrowser控件访问的 Web 页上的脚本调用window.close方法时,TWebBrowser控件可能会在窗体中消失.我们的程序应该对此作出反应,否则再次访问TWebBrowser控 ...
- 4.ClassLink - 一种新型的VPC 经典网络的连接方式
阿里云CLassLink文档地址:https://help.aliyun.com/document_detail/65412.html?spm=a2c4g.11186623.2.9.41a25a07F ...
- hiberate 配置相关
<hibernate-configuration> <session-factory name="mySessionFactory"> <proper ...
- 21.struts-Action配置.md
目录 1.Action开发方式 2.通配符 访问地址 [toc] 3.常量 后缀 指定默认编码集,作用于HttpServletRequest的setCharacterEncoding方法和freema ...
- C# 反射获取所有视图
原地址:忘了 controller 的 action 加上属性 [System.ComponentModel.Description("菜单列表")] 且 返回值为 Syste ...
- idea中maven中jdk版本的选择(转)
转自:https://www.cnblogs.com/joshul/p/6222398.html IntelliJ IDEA中Maven项目的默认JDK版本 在IntelliJ IDEA 15中使 ...
- Centos 7升级内核
检查当前 CentOS 系统内核版本 uname -sr 在 CentOS 7 上启用 ELRepo 仓库 rpm --import https://www.elrepo.org/RPM-GPG-KE ...