前言

表单在整个系统中的作用相当重要,这里主要扯下响应表单的实现方式。

首先须要操作表单的模块引入这两个模块。

import { FormsModule, ReactiveFormsModule } from '@angular/forms';


表单控件响应的几种状态

模板驱动表单依赖FormsModule,数据驱动的表单依赖FormsModule,ReactiveFormsModule

一般做表单校验及操作推荐用数据驱动的方式,好维护和理解。。


模板驱动

模板驱动:主要是依赖[(ngModel)]#scope_var以及原生表单控件属性(require,minlenght,maxlength等)来操作表单的那的值亦或者校验

  • 一个最简单的样例
  1. <!--#UserName 是局部变量,若是有ngmodel,拿到的就是一个响应对象。若是非ngmodel绑定的,则是dom元素代码-->
  2. <!--testform这个局部变量保存了表单的全部相关信息-->
  3. <!--ngSubmit是用来触发表单提交的-->
  4. <!--ngModel相应变量的值-->
  5. <!--$event是原生dom对象-->
  6. <form #testform="ngForm" (ngSubmit)="Submit(testform.value,testform.valid)">
  7. <label for="username">Name</label>
  8. <input type="text" id="username" #UserName="username" class="form-control"
  9. required minlength="4" maxlength="24"
  10. name="username" [(ngModel)]="username" [ngModelChange]="validate($event)">
  11. <div *ngIf="UserName.valid || (UserName.pristine && !testform.submitted)">
  12. 您输入的值有误,请又一次输入
  13. </div>
  14. <button type="submit" >提交</button>
  15. </form>

有两种方式处理来对上面的表单做校验;

  1. Submit()函数内,在点击提交的时候对整个表单一一去推断,传统方式基本这样
  2. 每一个控件输入的时候相应去触发相应的事件做校验,比方[ngModelChange]来处理双向绑定的值校验

数据驱动(Reactive Form)

响应式表表单:原理是一開始就构建整个表单,表单的值通过特殊指令formControlName一一关联(相似ngModel);

相关名词

- FormGroup: 用来追踪表单控件有效状态及值 =》 能够理解为获取且能够操作整个表单的数据

- FormBuilder:表单数据构建工具[构建初始表单],简化构建代码(包含了new FormGroup(),new FormControl(),new FormArray()),FormGroup()内置多种校验方式

- formControlName: 同步与FormGroup构建表单内同样字段的值!

项目中的案例

  • html
  1. <div [@flyIn]="true">
  2. <div class="beautify-form" *ngIf="!showLoading">
  3. <div class="page-header">
  4. 欢迎登录
  5. </div>
  6. <form [formGroup]="form" (ngSubmit)="onSubmit(form)">
  7. <div class="form-group" [ngClass]="{ 'has-danger': form.controls.UserName.invalid && form.controls.UserName.value ,'has-success': form.controls.UserName.valid && form.controls.UserName.value }">
  8. <div class="input-group input-group-lg">
  9. <span class="input-group-addon fpd fpd-ordinarylogin1"></span>
  10. <input type="text" class="form-control" formControlName="UserName" placeholder="手机号码 \ 邮箱 ">
  11. </div>
  12. <div class="form-control-feedback" *ngIf="(form.controls.UserName.dirty || form.controls.UserName.pristine) && form.controls.UserName.invalid && form.controls.UserName.value">账号不符合规范</div>
  13. <div class="form-control-feedback" *ngIf="(form.controls.UserName.dirty || form.controls.UserName.pristine) && form.controls.UserName.valid && form.controls.UserName.value">账号符合规范</div>
  14. </div>
  15. <div class="form-group" [ngClass]="{ 'has-danger': form.controls.PassWord.invalid && form.controls.PassWord.value ,'has-success': form.controls.PassWord.valid && form.controls.PassWord.value }">
  16. <div class="input-group input-group-lg">
  17. <span class="input-group-addon fpd fpd-mima"></span>
  18. <input type="PassWord" class="form-control" formControlName="PassWord" placeholder="请输入password">
  19. </div>
  20. <div class="form-control-feedback" *ngIf="(form.controls.PassWord.dirty || form.controls.PassWord.pristine) && form.controls.PassWord.invalid && form.controls.PassWord.value ">password不符合规范,请又一次输入</div>
  21. <div class="form-control-feedback" *ngIf="(form.controls.PassWord.dirty || form.controls.PassWord.pristine) && form.controls.PassWord.valid && form.controls.PassWord.value ">password符合规范</div>
  22. </div>
  23. <div class="form-group ">
  24. <div class="flex">
  25. <div class="beautify-wrap flex-wrap">
  26. <input type="checkbox" class="beautify-checkbox" name="rememberme" id="rememberAccount" formControlName="rememberAccount">
  27. <label for="rememberAccount"></label>记住账号
  28. </div>
  29. <!--<a [routerLink]="['/account/reset-pw']">忘记password</a>-->
  30. </div>
  31. </div>
  32. <div class="message-tips" *ngIf="messageTips">
  33. <i class="fpd fpd-error"></i> {{messageTips}}
  34. </div>
  35. <div class="form-group ">
  36. <button class="btn btn-lg btn-outline-success btn-block" type="submit" [disabled]="form.invalid">登录</button>
  37. </div>
  38. <div class="form-group">
  39. <span class="noaccount-notify">没有账号?点击</span><a [routerLink]="['/account/collect']" class="collect-user">用户登记</a>
  40. </div>
  41. </form>
  42. </div>
  43. <div class="loading" *ngIf="showLoading">
  44. <app-mit-loading [option]="'load4'"></app-mit-loading>
  45. </div>
  46. </div>
  • component.ts
  1. import { Component, OnInit, OnDestroy } from '@angular/core';
  2. import { FormGroup, Validators, FormBuilder } from '@angular/forms'; // 引入表单的一些特性
  3. import { Router } from '@angular/router';
  4. import { AccountService } from '../../services/account.service';
  5. import { environment } from '../../../../../environments/environment';
  6. import { flyIn } from '../../../../animation/flyIn';
  7. import { Observable } from 'rxjs/Observable';
  8. @Component({
  9. selector: 'app-login',
  10. templateUrl: './login.component.html',
  11. styleUrls: ['./login.component.scss'],
  12. animations: [flyIn]
  13. })
  14. export class LoginComponent implements OnInit, OnDestroy {
  15. public form: FormGroup; // 表单对象
  16. public showLoading = false;
  17. public messageTips: string;
  18. public login_subscribe: any;
  19. // Validators的写法注意事项
  20. // v2.x版本号这种写法是可行的。v4有调整,不然不会生效
  21. // 'UserName':'', [ Validators.compose([Validators.minLength(6)]
  22. // v4+ , 第一位的''代表这个元素初始化构建为空值,相似未输入状态
  23. // 'UserName': ['', Validators.compose([Validators.minLength(6)]
  24. // Validators可选參数
  25. // 1. required :必须验证的。返回布尔值
  26. // 2. minLength : 最小长度
  27. // 3. maxLenght: 最大长度
  28. // 4. nullValidator : 空值推断
  29. // 5. coompose :多重推断组合。以下有写法
  30. // 6. pattern是支持正则模式,正则谨记转义转义转义
  31. constructor(private fb: FormBuilder, private router: Router, private account: AccountService) {
  32. this.form = fb.group({
  33. 'UserName': ['', Validators.compose([Validators.minLength(6) || Validators.pattern('(0|86|17951)?(-)?1[3,4,5,7,8,9]\\d{9}') || Validators.pattern('[\\.a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\\.[a-zA-Z0-9_-]+)+')])],
  34. 'PassWord': ['', Validators.compose([Validators.required, Validators.pattern('\\w{8,16}')])],
  35. 'rememberAccount': ['']
  36. });
  37. }
  38. ngOnInit() {
  39. }
  40. // 登录事件
  41. onSubmit(e) {
  42. this.showLoading = true;
  43. this.login_subscribe = this.account.login(e.value).subscribe((res) => {
  44. console.log('省略。
  45. 。。。。')
  46. }, (err) => {
  47. this.showLoading = false;
  48. });
  49. }
  50. ngOnDestroy() {
  51. if (this.login_subscribe) {
  52. this.login_subscribe.unsubscribe();
  53. }
  54. }
  55. }

效果图


嵌套表单

有些时候我们接口数据层次不可能唯独一层,出现两层三层都有可能;

这时候须要我们构建一个嵌套表单。。。

  • html

v2-的写法:表单的取值能够用controls直接点出来

  1. <div class="custom-card">
  2. <div class="custom-card-body">
  3. <form [formGroup]="form" (ngSubmit)="onSubmit(form.value)">
  4. <div class="row" formGroupName="RuleContent">
  5. <div class="col-sm-12 col-md-12 col-lg-8 offset-lg-2">
  6. <div class="form-group row" [ngClass]="{ 'has-danger': form.controls.RuleContent.controls.FenceName.invalid && form.controls.RuleContent.controls.FenceName.value ,'has-success': form.controls.RuleContent.controls.FenceName.valid && form.controls.RuleContent.controls.FenceName.value }">
  7. <label tooltip="" class="col-sm-10 col-md-3 form-control-label col-lg-3 star">速度栅栏名称</label>
  8. <div class="col-sm-8 col-md-6 col-lg-6">
  9. <input type="text" class="form-control" formControlName="FenceName" placeholder="栅栏名称">
  10. </div>
  11. <div class="col-2 col-sm-4 col-lg-3 flex-align-center">
  12. 不超过十个字
  13. </div>
  14. </div>
  15. <div class="form-group row" [ngClass]="{ 'has-danger': form.controls.RuleContent.controls.MaxSpeed.invalid && form.controls.RuleContent.controls.MaxSpeed.value ,'has-success': form.controls.RuleContent.controls.MaxSpeed.valid && form.controls.RuleContent.controls.MaxSpeed.value }">
  16. <label tooltip="" class="col-sm-10 col-md-3 form-control-label col-lg-3 star">速度阈值</label>
  17. <div class="col-sm-8 col-md-6 col-lg-6">
  18. <input type="number" class="form-control" min="1" formControlName="MaxSpeed" placeholder="整数">
  19. </div>
  20. <div class="col-2 col-sm-4 col-lg-3 flex-align-center">
  21. km/h
  22. </div>
  23. </div>
  24. <div class="form-group row">
  25. <div class="col-12 col-sm-10 col-md-6 offset-sm-2 offset-md-4 offset-lg-3">
  26. <button type="submit" class="btn btn-primary" [disabled]="form.invalid">保存</button>
  27. <button type="button" class="btn btn-secondary" (click)="back()">取消</button>
  28. </div>
  29. </div>
  30. </div>
  31. </div>
  32. </form>
  33. </div>
  34. </div>

v4+的写法 :嵌套表单的取值必须用.get()来获取,不然会报错误。详细原因是api修改了,看下官方文档就知道,修改了挺多(不只这块)

  1. <div class="custom-card">
  2. <div class="custom-card-body">
  3. <form [formGroup]="form" (ngSubmit)="onSubmit(form.value)">
  4. <div class="row" formGroupName="RuleContent">
  5. <div class="col-sm-12 col-md-12 col-lg-8 offset-lg-2">
  6. <div class="form-group row" [ngClass]="{ 'has-danger': form.get('RuleContent.FenceName').invalid && form.get('RuleContent.FenceName').value ,'has-success': form.get('RuleContent.FenceName').valid && form.get('RuleContent.FenceName').value }">
  7. <label tooltip="" class="col-sm-10 col-md-3 form-control-label col-lg-3 star">速度栅栏名称</label>
  8. <div class="col-sm-8 col-md-6 col-lg-6">
  9. <input type="text" class="form-control" formControlName="FenceName" placeholder="栅栏名称">
  10. </div>
  11. <div class="col-2 col-sm-4 col-lg-3 flex-align-center">
  12. 不超过十个字
  13. </div>
  14. </div>
  15. <div class="form-group row" [ngClass]="{ 'has-danger': form.get('RuleContent.MaxSpeed').invalid && form.get('RuleContent.MaxSpeed').value ,'has-success': form.get('RuleContent.MaxSpeed').valid && form.get('RuleContent.MaxSpeed').value }">
  16. <label tooltip="" class="col-sm-10 col-md-3 form-control-label col-lg-3 star">速度阈值</label>
  17. <div class="col-sm-8 col-md-6 col-lg-6">
  18. <input type="number" class="form-control" min="1" formControlName="MaxSpeed" placeholder="整数">
  19. </div>
  20. <div class="col-2 col-sm-4 col-lg-3 flex-align-center">
  21. km/h
  22. </div>
  23. </div>
  24. <div class="form-group row">
  25. <div class="col-12 col-sm-10 col-md-6 offset-sm-2 offset-md-4 offset-lg-3">
  26. <button type="submit" class="btn btn-primary" [disabled]="form.invalid">保存</button>
  27. <button type="button" class="btn btn-secondary" (click)="back()">取消</button>
  28. </div>
  29. </div>
  30. </div>
  31. </div>
  32. </form>
  33. </div>
  34. </div>
  • components.ts

  1. import { Component, OnInit } from '@angular/core';
  2. import { Router, ActivatedRoute } from '@angular/router';
  3. import { FormGroup, FormControl, Validators, FormBuilder } from '@angular/forms'; // 引入表单的一些特性
  4. // 动画
  5. import { fadeIn } from '../../../../../animation/fadeIn';
  6. // 服务
  7. import { SpeedFenceService } from '../speed-fence.service';
  8. import { EventsService } from '../../../../../services/events-service.service';
  9. @Component({
  10. selector: 'app-modify',
  11. templateUrl: './modify.component.html',
  12. styleUrls: ['./modify.component.scss'],
  13. animations: [fadeIn]
  14. })
  15. export class ModifyComponent implements OnInit {
  16. public form: FormGroup;
  17. public getId: any;
  18. public id: number;
  19. constructor(
  20. private speedFenceService: SpeedFenceService,
  21. private eventsService: EventsService,
  22. private router: Router,
  23. private activatedRoute: ActivatedRoute,
  24. private fb: FormBuilder
  25. ) {
  26. this.form = fb.group({
  27. 'ID': 0,
  28. 'RuleContent': this.fb.group({
  29. 'MaxSpeed': [0, Validators.compose([Validators.required, Validators.pattern('(([4-9][0-9])|(1[0-1][0-9])|(120))')])],
  30. 'FenceName': ['', Validators.compose([Validators.required, Validators.minLength(2), Validators.maxLength(10)])],
  31. })
  32. });
  33. }
  34. ngOnInit() {
  35. this.checkAction();
  36. // console.log(this.form);
  37. }
  38. // 获取ID
  39. checkAction() {
  40. this.activatedRoute.params.subscribe((params: { id: string }) => {
  41. console.log(params);
  42. if (params.id) {
  43. console.log(this.id);
  44. this.id = parseInt(params.id, 10);
  45. this.form.controls['ID'].setValue(this.id);
  46. this.GetSpeedFenceSettingByFenceId({ FenceId: parseInt(params.id, 10) });
  47. }
  48. });
  49. }
  50. GetSpeedFenceSettingByFenceId(data) {
  51. this.speedFenceService.GetSpeedFenceSettingByFenceId(data).subscribe(
  52. res => {
  53. if (res.State) {
  54. this.form.controls['RuleContent'].setValue({
  55. 'MaxSpeed': res.Data.RuleContent.MaxSpeed || '',
  56. 'FenceName': res.Data.RuleContent.FenceName || '',
  57. });
  58. }
  59. },
  60. err => { }
  61. );
  62. }
  63. onSubmit(form) {
  64. console.log('此处省略。。。。
  65. 。');
  66. }
  67. // 取消
  68. back() {
  69. if (this.id) {
  70. this.router.navigate(['../../'], { relativeTo: this.activatedRoute });
  71. } else {
  72. this.router.navigate(['../'], { relativeTo: this.activatedRoute });
  73. }
  74. }
  75. }

总结

多看手冊多动手,才是真理。。

有不足之处或者错误之处请留言指出,会及时跟进修正。。

谢谢

Angular 2 + 折腾记 :(7) 初步了解表单:模板驱动及数据驱动及脱坑要点的更多相关文章

  1. flask用宏渲染表单模板时,表单提交后,如果form.validate_on_submit()返回的是false的可能原因

    flask用宏渲染表单模板时,表单提交后,提交的内容符合DataRequired()校验, 但是form.validate_on_submit()返回的是False, 原因可能是表单模板中的<f ...

  2. Angular基础(五) 内建指令和表单

    ​ Angular提供了一些内建的指令,可以作为属性添加给HTML元素,以动态控制其行为. 一.内建指令 a) *ngIf,可以根据条件来显示或隐藏HTML元素. <div *ngIf='a&g ...

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

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

  4. Angular : 响应式编程, 组件间通信, 表单

    Angular 响应式编程相关 ------------------------------------------------------------------------------------ ...

  5. React躬行记(7)——表单

    表单元素是一类拥有内部状态的元素,这些状态由其自身维护,通过这类元素可让用户与Web应用进行交互.HTML中的表单元素(例如<input>.<select>和<radio ...

  6. Angular.js通过bootstrap实现经典的表单提交

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <link rel= ...

  7. form 表单模板

    <div class="modal-dialog modal-lg"> //大布局modal-lg <div class="modal-content& ...

  8. HTML 表单模板

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  9. Angular 从入坑到挖坑 - 表单控件概览

    一.Overview angular 入坑记录的笔记第三篇,介绍 angular 中表单控件的相关概念,了解如何在 angular 中创建一个表单,以及如何针对表单控件进行数据校验. 对应官方文档地址 ...

随机推荐

  1. 初始化android studio的方法

    有可能是在一次android studio被我强制关闭以后,我的android studio就出现了各种诡异的问题,项目无法运行,新建的项目报错,等等.抓狂~~ 于是想到把android studio ...

  2. openfiler在esxi下的安装配置

    注意分区的时候如果硬盘太小自动分区会导致分配的卷大小不够用 后改为如下: 以root登录: 应该以openfiler登录,口令是password 也可以导入虚拟机安装 升级虚拟机硬件版本 终端登录用户 ...

  3. innosetup完整脚本

    #define MyAppName "Somarto"#define MyAppVersion "1.0.0"#define MyAppPublisher &q ...

  4. sqlalchemy结果转json

    网上搜了下,http://blog.csdn.net/liu_xing_hui/article/details/8956107 介绍的很详细,自动一个Encoder给json的dump方法使用,能够实 ...

  5. Emacs 使用graphviz-dot-mode创建架构图

    安装 首先要安装graphviz-dot-mode模块,假设list-packages的网站国内无法訪问,改为手动下载graphviz-dot-model.el.放到~/.emacs.d/文件夹下. ...

  6. (素材源代码) 猫猫学iOS 之UIDynamic重力、弹性碰撞吸附等现象牛逼Demo

    猫猫分享,必须精品 原创文章,欢迎转载. 转载请注明:翟乃玉的博客 地址:http://blog.csdn.net/u013357243 一:效果 二:代码 #import "ViewCon ...

  7. js 内置函数 内置对象

    1.内置函数 Object Array Boolean Number String Function Date RegExp Error 2.内置对象 Date JSON

  8. Android——代码中使用颜色值

    android中设置颜色时,可以直接使用颜色值来设置: view.setBackgroundColor(Color.parseColor("#颜色值"));

  9. DWR组件——基于远程过程调用实现Ajax

    转载请注明原文地址:http://www.cnblogs.com/ygj0930/p/6686115.html  一:DWR的用途 DWR(Direct Web Remoting)是一个Web远程调用 ...

  10. vim note write

    Try: :vert sb N which will open a left vertical split (by default, unless you have modified some opt ...