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简 ...
随机推荐
- mac 命令行上传文件,mac tar.gz命令压缩
在mac上可以直接打开命令行给服务器上传文件,注意是本地的命令行,不是服务器的命令行,我就走了绕路 命令可以看这里https://www.cnblogs.com/hitwtx/archive/2011 ...
- 机器学习入门-文本特征-使用LDA主题模型构造标签 1.LatentDirichletAllocation(LDA用于构建主题模型) 2.LDA.components(输出各个词向量的权重值)
函数说明 1.LDA(n_topics, max_iters, random_state) 用于构建LDA主题模型,将文本分成不同的主题 参数说明:n_topics 表示分为多少个主题, max_i ...
- 子类中的成员函数覆盖父类(name hiding)
只要子类中出现了和父类中同名的函数,父类中的所有这个名字的函数,就被屏蔽了. 静态函数成员也是如此?经过代码验证,确实如此. #include <iostream> using names ...
- js 提示框的实现---组件开发之(二)
接着第上一个,在js文件里再增加一个 popModal 模块,实现弹框效果 css 代码: .alert { padding: 15px; margin-bottom: 20px; border: 1 ...
- css:margin和padding的百分之使用
#app { position: fixed; width: 94%; height: 100%; background: pink; padding: 0px 3% 0px 3%;} 如上代码,最终 ...
- C# 容器重用避免GC 的论证
var lst = new List<int>(); lst.Capacity = ; var cnt = lst.Count; var cp = lst.Capacity; ; i< ...
- 一种比较low的linux的hung分析
在调试一个功能的时候,发现了两种hung,以前认为的hung肯定是softlock导致的,后来才发现不一定要有lock这种结构,但是有类似于锁的功能的时候,也可能触发hung,为了避免大家走弯路,故记 ...
- How to Pronounce the Word ARE
How to Pronounce the Word ARE Share Tweet Share Tagged With: ARE Reduction Study the ARE reduction. ...
- ARP工作过程、ARP欺骗的原理和现象、如何防范ARP欺骗
地址解析协议(Address Resolution Protocol,ARP)是在仅知道主机的IP地址时确定其物理地址的一种协议. 下面假设在一个局域网内,主机A要向主机B发送IP数据报. ARP ...
- Hibernate学习笔记1.1(简单插入数据)
Hibernate是把以前的jdbc连接数据库的操作进行了一系列友好的封装,最好只用调用save即可,即将sql语句的这部分操作转化为面向对象的 Hibernate资源准备: 文档目录结构: 1.网址 ...