angular2+折腾记:初步了解表单:模板驱动及数据驱动及脱坑要点

crper      2022-02-08     484

关键词:

前言

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

首先需要操作表单的模块引入这两个模块;

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

表单控件响应的几种状态

模板驱动表单依赖FormsModule,

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

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

模板驱动

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

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

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

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

数据驱动(Reactive Form)

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

相关名词

  • FormGroup: 用来追踪表单控件有效状态及值 =》 可以理解为获取且可以操作整个表单的数据
  • FormBuilder:表单数据构建工具[构建初始表单],简化构建代码(包括了new FormGroup(),new FormControl(),new FormArray()),FormGroup()内置多种校验方式
  • formControlName: 同步与FormGroup构建表单内相同字段的值!

项目中的案例

html

<div [@flyIn]="true">
  <div class="beautify-form" *ngIf="!showLoading">
    <div class="page-header">
      欢迎登录
    </div>
    <form [formGroup]="form" (ngSubmit)="onSubmit(form)">
      <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 }">
        <div class="input-group input-group-lg">
          <span class="input-group-addon fpd fpd-ordinarylogin1"></span>
          <input type="text" class="form-control" formControlName="UserName" placeholder="手机号码 \ 邮箱 ">
        </div>
        <div class="form-control-feedback" *ngIf="(form.controls.UserName.dirty || form.controls.UserName.pristine) && form.controls.UserName.invalid && form.controls.UserName.value">账号不符合规范</div>
        <div class="form-control-feedback" *ngIf="(form.controls.UserName.dirty || form.controls.UserName.pristine) && form.controls.UserName.valid && form.controls.UserName.value">账号符合规范</div>
      </div>
      <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 }">
        <div class="input-group input-group-lg">
          <span class="input-group-addon fpd fpd-mima"></span>
          <input type="PassWord" class="form-control" formControlName="PassWord" placeholder="请输入密码">
        </div>
        <div class="form-control-feedback" *ngIf="(form.controls.PassWord.dirty || form.controls.PassWord.pristine) && form.controls.PassWord.invalid && form.controls.PassWord.value ">密码不符合规范,请重新输入</div>
        <div class="form-control-feedback" *ngIf="(form.controls.PassWord.dirty || form.controls.PassWord.pristine) && form.controls.PassWord.valid && form.controls.PassWord.value ">密码符合规范</div>
      </div>
      <div class="form-group ">
        <div class="flex">
          <div class="beautify-wrap flex-wrap">
            <input type="checkbox" class="beautify-checkbox" name="rememberme" id="rememberAccount" formControlName="rememberAccount">
            <label for="rememberAccount"></label>记住账号
          </div>
          <!--<a [routerLink]="['/account/reset-pw']">忘记密码</a>-->
        </div>
      </div>
      <div class="message-tips" *ngIf="messageTips">
        <i class="fpd fpd-error"></i> {{messageTips}}
      </div>
      <div class="form-group ">
        <button class="btn btn-lg btn-outline-success btn-block" type="submit" [disabled]="form.invalid">登录</button>
      </div>
      <div class="form-group">
        <span class="noaccount-notify">没有账号?点击</span><a [routerLink]="['/account/collect']" class="collect-user">用户登记</a>
      </div>
    </form>
  </div>
  <div class="loading" *ngIf="showLoading">
    <app-mit-loading [option]="'load4'"></app-mit-loading>
  </div>
</div>


component.ts

import { Component, OnInit, OnDestroy } from '@angular/core';
import { FormGroup, Validators, FormBuilder } from '@angular/forms'; // 引入表单的一些特性
import { Router } from '@angular/router';
import { AccountService } from '../../services/account.service';
import { environment } from '../../../../../environments/environment';
import { flyIn } from '../../../../animation/flyIn';
import { Observable } from 'rxjs/Observable';
@Component({
  selector: 'app-login',
  templateUrl: './login.component.html',
  styleUrls: ['./login.component.scss'],
  animations: [flyIn]
})
export class LoginComponent implements OnInit, OnDestroy {
  public form: FormGroup; // 表单对象
  public showLoading = false;
  public messageTips: string;
  public login_subscribe: any;
  
  
  // Validators的写法注意事项
  // v2.x版本这样的写法是可行的,v4有调整,不然不会生效
  // 'UserName':'', [ Validators.compose([Validators.minLength(6)] 
  
  // v4+ , 第一位的''代表这个元素初始化构建为空值,类似未输入状态
  // 'UserName': ['', Validators.compose([Validators.minLength(6)]
  
  // Validators可选参数
  // 1. required :必须验证的,返回布尔值
  // 2. minLength : 最小长度
  // 3. maxLenght: 最大长度
  // 4. nullValidator : 空值判断
  // 5. coompose :多重判断组合,下面有写法 
  // 6. pattern是支持正则模式,正则谨记转义转义转义
  constructor(private fb: FormBuilder, private router: Router, private account: AccountService) {
    this.form = fb.group({
      '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_-]+)+')])],
      'PassWord': ['', Validators.compose([Validators.required, Validators.pattern('\\w{8,16}')])],
      'rememberAccount': ['']
    });
  }


  ngOnInit() {

  }

  // 登录事件
  onSubmit(e) {

    this.showLoading = true;
    this.login_subscribe = this.account.login(e.value).subscribe((res) => {
     console.log('省略。。。。。。')
    }, (err) => {
      this.showLoading = false;
    });
  }

  ngOnDestroy() {
    if (this.login_subscribe) {
      this.login_subscribe.unsubscribe();
    }
  }

}


效果图

嵌套表单

有些时候我们接口数据层次不可能只有一层,出现两层三层都有可能,这时候需要我们构建一个嵌套表单。。。

html

  • v2-的写法:表单的取值可以用controls直接点出来
<div class="custom-card">
  <div class="custom-card-body">
    <form [formGroup]="form" (ngSubmit)="onSubmit(form.value)">
      <div class="row" formGroupName="RuleContent">
        <div class="col-sm-12 col-md-12 col-lg-8  offset-lg-2">
          <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 }">
            <label tooltip="" class="col-sm-10 col-md-3 form-control-label col-lg-3 star">速度栅栏名称</label>
            <div class="col-sm-8 col-md-6 col-lg-6">
              <input type="text" class="form-control" formControlName="FenceName" placeholder="栅栏名称">
            </div>
            <div class="col-2 col-sm-4 col-lg-3 flex-align-center">
              不超过十个字
            </div>
          </div>
          <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 }">
            <label tooltip="" class="col-sm-10 col-md-3 form-control-label col-lg-3 star">速度阈值</label>
            <div class="col-sm-8 col-md-6 col-lg-6">
              <input type="number" class="form-control" min="1" formControlName="MaxSpeed" placeholder="整数">
            </div>
            <div class="col-2 col-sm-4 col-lg-3 flex-align-center">
              km/h
            </div>
          </div>
          <div class="form-group row">
            <div class="col-12 col-sm-10 col-md-6 offset-sm-2 offset-md-4 offset-lg-3">
              <button type="submit" class="btn btn-primary" [disabled]="form.invalid">保存</button>
              <button type="button" class="btn btn-secondary" (click)="back()">取消</button>
            </div>
          </div>
        </div>
      </div>
    </form>

  </div>
</div>


  • v4+的写法 :嵌套表单的取值必须用.get()来获取,不然会报错误,具体原因是api改动了,看下官方文档就知道,改动了挺多(不仅仅这块)
<div class="custom-card">
  <div class="custom-card-body">
    <form [formGroup]="form" (ngSubmit)="onSubmit(form.value)">
      <div class="row" formGroupName="RuleContent">
        <div class="col-sm-12 col-md-12 col-lg-8  offset-lg-2">
          <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 }">
            <label tooltip="" class="col-sm-10 col-md-3 form-control-label col-lg-3 star">速度栅栏名称</label>
            <div class="col-sm-8 col-md-6 col-lg-6">
              <input type="text" class="form-control" formControlName="FenceName" placeholder="栅栏名称">
            </div>
            <div class="col-2 col-sm-4 col-lg-3 flex-align-center">
              不超过十个字
            </div>
          </div>
          <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 }">
            <label tooltip="" class="col-sm-10 col-md-3 form-control-label col-lg-3 star">速度阈值</label>
            <div class="col-sm-8 col-md-6 col-lg-6">
              <input type="number" class="form-control" min="1" formControlName="MaxSpeed" placeholder="整数">
            </div>
            <div class="col-2 col-sm-4 col-lg-3 flex-align-center">
              km/h
            </div>
          </div>
          <div class="form-group row">
            <div class="col-12 col-sm-10 col-md-6 offset-sm-2 offset-md-4 offset-lg-3">
              <button type="submit" class="btn btn-primary" [disabled]="form.invalid">保存</button>
              <button type="button" class="btn btn-secondary" (click)="back()">取消</button>
            </div>
          </div>
        </div>
      </div>
    </form>

  </div>
</div>


components.ts


import { Component, OnInit } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
import { FormGroup, FormControl, Validators, FormBuilder } from '@angular/forms'; // 引入表单的一些特性

// 动画
import { fadeIn } from '../../../../../animation/fadeIn';

// 服务
import { SpeedFenceService } from '../speed-fence.service';
import { EventsService } from '../../../../../services/events-service.service';


@Component({
  selector: 'app-modify',
  templateUrl: './modify.component.html',
  styleUrls: ['./modify.component.scss&

angular2模板驱动的表单验证问题

】angular2模板驱动的表单验证问题【英文标题】:angular2templatedrivenformvalidationissue【发布时间】:2018-03-0103:32:05【问题描述】:我对angular2模板驱动的表单验证有问题,以下是我的表单的代码。<h1>page_title</h1><div*ngIf="suc... 查看详情

Angular 2 模板驱动的表单组验证

】Angular2模板驱动的表单组验证【英文标题】:Angular2templatedrivenformgroupvalidation【发布时间】:2017-06-2707:57:15【问题描述】:我有一个关于angular2模板驱动表单的问题。我已经设置了其中一个表单,如果表单组中的一个输入无效,... 查看详情

atom编辑器折腾记

http://blog.csdn.net/bomess/article/category/3202419/2 Atom编辑器折腾记_(1)介绍下载安装Atom编辑器折腾记_(2)基础了解使用Atom编辑器折腾记_(3)插件主题推荐Atom编辑器折腾记_(4)按键绑定keymapAtom编辑器折腾记_(5)记住上一次打开的目录及浏... 查看详情

带有 ngFor 输入的 Angular 2 模板驱动表单

】带有ngFor输入的Angular2模板驱动表单【英文标题】:Angular2templatedrivenformwithngForinputs【发布时间】:2017-01-2915:48:06【问题描述】:是否可以在模板驱动的表单中使用ngFor创建输入字段,并使用#name="ngModel"之类的东西来在另一个标... 查看详情

atom编辑器折腾记

(1)介绍下载安装(2)基础了解使用(3)插件主题推荐(4)按键绑定keymap(5)记住上一次打开的目录及浏览器预览功能实现(6)config.cson基础教程(7)Emmet实例教程(8)分屏操作(9)实用侧边栏插件[仿MacOSfinder](10)CSScomb增强版[CSS/LESS/SASS](11)编辑器实... 查看详情

angular2+折腾记:动手写一个不怎么靠谱的上传组件

前言上传功能在任何一个网站中的地位都是举足轻重的,这篇文章主要扯下如何实现一个上传组件效果图所具有的功能支持的图片格式(不传参则使用默认参数)支持的图片大小图片上传之前会被压缩(前端)–异步加载进来上传过程... 查看详情

Angular 2中的模板驱动形式和反应形式有啥区别

】Angular2中的模板驱动形式和反应形式有啥区别【英文标题】:Whatarethedifferencesbetweentemplatedrivenformsandreactiveformsinangular2Angular2中的模板驱动形式和反应形式有什么区别【发布时间】:2017-12-2920:03:27【问题描述】:在Angular2中,模... 查看详情

webcomponents折腾记

...资料参考,就是官方英文网站貌似都没看到有文档说明,折腾起来甚是费劲。最开始对webcomponents技术还很懵懂,只知道它由几个子技术组成, 查看详情

angular2中的表格

】angular2中的表格【英文标题】:FormsInangular2【发布时间】:2016-03-2420:00:58【问题描述】:对如何在angular2beta中使用表单(模板或模态驱动)有点困惑。目前我正在使用模态驱动的表单,但我的form.html出现了一些错误:<form[ngFo... 查看详情

Angular 2阻止输入以模板驱动的形式提交

】Angular2阻止输入以模板驱动的形式提交【英文标题】:Angular2prevententerfromsubmittingintemplate-drivenform【发布时间】:2017-03-3122:16:14【问题描述】:我有使用模板驱动蓝图的表单,所以是这样的:<form#myForm="ngForm"ngSubmit="save(myForm.va... 查看详情

Angular2 模板驱动的异步验证器

】Angular2模板驱动的异步验证器【英文标题】:Angular2templatedrivenasyncvalidator【发布时间】:2016-07-1402:59:42【问题描述】:我在以模板驱动形式定义异步验证器时遇到问题。目前我有这个输入:<inputtype="text"ngControl="email"[(ngModel)]=... 查看详情

qlikview基础设置及初步了解

改变语言环境 开发工具条勾选出来创建selectionbox创建searchbox编辑脚本重加载数据基本联动思路:tableview tableview  查看详情

Angular 2 - 自定义表单控件 - 禁用

】Angular2-自定义表单控件-禁用【英文标题】:Angular2-CustomFormControl-Disable【发布时间】:2017-03-0223:33:00【问题描述】:我使用ControlValueAccessor创建了一个自定义控件,该控件由input[type=text]和一个日期选择器组成。当我在模板驱动... 查看详情

Angular 2 反应式表单与模板表单

】Angular2反应式表单与模板表单【英文标题】:Angular2ReactiveFormsvsTemplateForms【发布时间】:2017-11-1708:32:21【问题描述】:我们正在启动一个新的Angular2项目,并正在考虑是否使用反应式表单或模板表单。背景阅读:https://angular.io/g... 查看详情

华硕飞行堡垒zx50安装ubunutu折腾记

...,,可恶的是,笔记本安装Linux系统往往比较麻烦,必须折腾很久才安装上,我手上这台笔记本也不例外。 我选择了UbuntuGnome作为要安装的系统,其原因有两点:1.Ubuntu对笔记本的支持比较好,驱动比较丰富2.我不喜欢Ubuntu的... 查看详情

以 angular2 模型驱动形式重用组件

】以angular2模型驱动形式重用组件【英文标题】:Reusecomponentsinangular2modeldrivenforms【发布时间】:2017-04-1423:09:19【问题描述】:我对angular2还很陌生,过去几天我一直在尝试使用模型驱动的表单创建可重用的表单组件假设我们有一... 查看详情

javascript学习js的初步了解

1、javascript的简介:     *javascript是一种基于对象和事件驱动的语言,主要应用于客户端。         --基于对象:             **提供了很多对象,可以直接使用。         -... 查看详情

jdbc的初步了解及使用(代码片段)

一、概念1、什么是JDBC?  JDBC(JavaDataBaseConnectivity,java数据库连接)是一种用于执行SQL语句的JavaAPI,可以为多种关系数据库提供统一访问,它由一组用Java语言编写的类和接口组成。JDBC提供了一种基准,据此可以构建更高级的... 查看详情