Angular 2 + 折腾记 :(7) 初步了解表单:模板驱动及数据驱动及脱坑要点

前言

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

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

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 :多重判断组合,下面有写法
  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_-][email protected][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‘],
  animations: [fadeIn]
})
export class ModifyComponent implements OnInit {
  public form: FormGroup;
  public getId: any;
  public id: number;
  constructor(
    private speedFenceService: SpeedFenceService,
    private eventsService: EventsService,
    private router: Router,
    private activatedRoute: ActivatedRoute,
    private fb: FormBuilder
  ) {
    this.form = fb.group({
      ‘ID‘: 0,
      ‘RuleContent‘: this.fb.group({
        ‘MaxSpeed‘: [0, Validators.compose([Validators.required, Validators.pattern(‘(([4-9][0-9])|(1[0-1][0-9])|(120))‘)])],
        ‘FenceName‘: [‘‘, Validators.compose([Validators.required, Validators.minLength(2), Validators.maxLength(10)])],
      })

    });
  }

  ngOnInit() {
    this.checkAction();
    // console.log(this.form);
  }

  // 获取ID
  checkAction() {
    this.activatedRoute.params.subscribe((params: { id: string }) => {
      console.log(params);
      if (params.id) {
        console.log(this.id);
        this.id = parseInt(params.id, 10);
        this.form.controls[‘ID‘].setValue(this.id);
        this.GetSpeedFenceSettingByFenceId({ FenceId: parseInt(params.id, 10) });
      }
    });
  }

  GetSpeedFenceSettingByFenceId(data) {
    this.speedFenceService.GetSpeedFenceSettingByFenceId(data).subscribe(
      res => {
        if (res.State) {
          this.form.controls[‘RuleContent‘].setValue({
            ‘MaxSpeed‘: res.Data.RuleContent.MaxSpeed || ‘‘,
            ‘FenceName‘: res.Data.RuleContent.FenceName || ‘‘,
          });
        }
      },
      err => { }
    );
  }

  onSubmit(form) {
    console.log(‘此处省略。。。。。‘);

  }

  // 取消
  back() {
    if (this.id) {
      this.router.navigate([‘../../‘], { relativeTo: this.activatedRoute });
    } else {
      this.router.navigate([‘../‘], { relativeTo: this.activatedRoute });
    }
  }

}

总结

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

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

时间: 2024-12-06 05:48:36

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

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

? Angular提供了一些内建的指令,可以作为属性添加给HTML元素,以动态控制其行为. 一.内建指令 a) *ngIf,可以根据条件来显示或隐藏HTML元素. <div *ngIf='a>b'>show</div>,只有在a>b的时候才会显示这个div. b)*ngSwitchCase,可以根据不同的条件,控制对应的HTML元素的显示. <div[ngSwitch]='myVar'> <div *ngSwitchCase="'a'&quo

angular 使用bootstratp模态框+响应式表单+响应式表单的验证+子窗关闭父窗口刷新

This is quite big project today 这次有以下几个功能: 1.ng-bootstrap模态框 2.响应式表单 3.响应式表单的验证 4.子窗关闭父窗口刷新 其实这几个哪一个都能写一个话题,事情太多了,时间紧任务重,一起写了吧: ng-bootstrap模态框所需要的条件(very very important),如果写错,查错非常的难哦,请慎重[反正我看不懂错误提示,出个错解决老办天]. 1.package.json加入dependencies: "@ng-boots

React躬行记(7)——表单

表单元素是一类拥有内部状态的元素,这些状态由其自身维护,通过这类元素可让用户与Web应用进行交互.HTML中的表单元素(例如<input>.<select>和<radio>等)在React中都有相应的组件实现,不仅如此,React还将它们分成两种:受控组件和非受控组件. 一.受控组件 受控组件(Controlled Component)是指那些受React控制的表单元素,其状态(value.checked等属性)的变更由组件的state管理.对于不同的表单元素,其受控组

初步了解表单操作

表单操作 获取表单 获取表单元素 获取表单元素的方式,大体上可以分为以下几种方式:1.Document对象提供的定位页面元素的一系列方法 <form id='myform' name='myform' class='login' action='#'></form> <script> var formId=document.getElementById('myform'); var formName=document.getElementsByName('myform'

【jeecg移动开发能力】表单移动开发能力,提供多套表单模板(移动端、PC端),支持自定义

[一].请假表单风格(单表)  [1]移动端风格 [2]PC端风格一   [3]PC端风格二    [二].订单管理表单风格(一对多)  [1]移动端风格 [2]PC端风格一  [3]PC端风格二

element ui 库中 表单部分 一个很容易忽略的坑!

如果在使用element-ui1.4.* 版本中的表单时,发现某些字段绑定不了值,或者checkbox在你绑定后一点就全选了或者全部选,又或者表单验证时候有些字段没动静,那么十有八九是: 1.el-form 标签绑定data中的form时候,请使用:model="form" 而不是v-model="form". 2.请给el-form-item 标签中加上prop属性. 一些很简单的问题,也是自己踩过的坑,希望能帮到大家.

Angular 2 + 折腾记 :(8) 动手写一个不怎么靠谱的上传组件

前言 上传功能在任何一个网站中的地位都是举足轻重的,这篇文章主要扯下如何实现一个上传组件 效果图 所具有的功能 支持的图片格式(不传参则使用默认参数) 支持的图片大小 图片上传之前会被压缩(前端) – 异步加载进来 上传过程会显示loading(loading组件)–就一些css3样式 支持组件高度设置,宽度自适应 支持标题设置 组件以模块形式导出 代码如下,相关逻辑请看注释. mit-upload.module.ts – 上传模块 // 这三个就不用再解释了 import { NgModule

表单验证&amp;lt;AngularJs&amp;gt;

经常使用的表单验证指令 1. 必填项验证 某个表单输入是否已填写,仅仅要在输入字段元素上加入HTML5标记required就可以: <input type="text" required />   2. 最小长度 验证表单输入的文本长度是否大于某个最小值,在输入字段上使用指令ng-minleng= "{number}": <input type="text" ng-minlength="5" /> 3.

基于Extjs的web表单设计器 第四节——控件拖放

接着上一节介绍控件拖放的设计. 通过前面的介绍知道,我们的区域类型的容器控件有三种:Card.Table.Mixed. Card 可以支持几乎所有的常用控件,包括:文本TextField.多文本TextArea.数字NumberField.金额NumberField.日期DateField.下拉树NetDropDown.按钮Button.复选框CheckBox.单选框Radio:Table可以支持的常用控件比Card稍微少一点,它不支持button类型的控件以及多文本TextArea控件:Mix