Angular表单处理

第七章 表单处理

学习内容:

模板式表单

响应式表单

表单验证

一.简述模板式表单与响应式表单的不同

Angular表单API

模板式表单简述:

表单数据模型通过组件模板中的相关指令定义,但是只
适用于一些简单的场景。比较死板

响应式表单简述:

通过typescript创建底层的数据模型。可通过特定的
指令将模板上的HTML元素和底层的数据模型相联系。比较灵活

两种表单在数据模型中的异同:

1.都需要数据模型来存储表单数据。
2.数据模型由angular/forms模块中的一些特定的类,
  如FormControl,FormGroup等组成。
3.模板式表单中,数据模型由组件模板中的指令隐式创建。
  响应式表单中,数据模型自己来创建,并与HTML元素相连接。
4.模板式表单中,不能访问这些类;而响应式可以。

模块引入的不同

1.模板式表单只需引入FormsModule模块
2.使用响应式,必须引入 FormsModule、
ReactiveFormsModule这两个模块。

二. 模板式表单

指令:来自FormsModule模块中

ngForm 作用:

1.ngForm指令可以发现所有标有 ‘ngModel’的子元素,并将其值添加到表单模型中
  创建FormGroup的实例,将数据存储在ngForm.value对象中。

2.任何标有“ngForm”指令的元素都会有此作用。不论是不是form表单元素

3.当不希望Angular接管表单时,可给表单标签添加“ngNoForm”指令。

4.ngForm指令可以被模板本地变量引用,来访问模板表单的实例

5.会阻止表单的提交并刷新,使用**ngSubmit**事件来提交
6.隐式创建FormGroup类型的实例

ngModel 作用:

1.ngModel是ngForm指令所在元素的子元素的字段,
  与name属性相关联(仅当添加name属性后才能被模板捕获,创建数据模型)。

2.会隐式创建FormControl的实例,来代表该字段,并用FormControl创建的对象存储其值。

3.ngModel代表一个字段,而不是双向绑定,不需要绑定变量,也不需要在组件中声明变量。

4.ngModel指令可以被模板本地变量引用,来访问模板表单的字段的值。

ngFormGroup 作用:

1.与ngForm类似,也会创建FormGroup的实例,该数据是嵌套在ngForm.value对象中的。

2.用来将有联系的表单元素放置于一块,并组成对象格式数据。

代码示例:

<form #myForm="ngForm" (ngSubmit)="createUser(myForm.value)">    // #myForm="ngForm" 模板本地变量引入ngForm指令
    
        <div>姓名:<input type="text" #myName="ngModel" ngModel name="myname"></div>
        
        <div>邮箱:<input type="email" ngModel name="email"></div>
        <div>手机号:<input type="number" ngModel name="mobile"></div>
        <div ngModelGroup="passwordInfo">
            <div>密码:<input type="password" ngModel name="password"></div>
            <div>确认密码:<input type="password" ngModel name="passwordConfirm"></div>
        </div>
        <button tyoe="submit">注册</button>
    </form>
    <div>
        {{myForm.value | json}}  // 模板本地变量引入ngForm
    </div>
    <div>
        {{myName.value | json}}  // 模板本地变量引入ngModel
    </div>
    createUser(info: any) {
        console.log(info);
    }
展示数据:
    { 
        "myname": "hello", 
        "email": "19242924@qq.com", 
        "mobile": 13479347937, 
        "passwordInfo": { "password": "12121", "passwordConfirm": "121212" } 
    }

    hello

三. 响应式表单:

响应式表单指令

1.编写响应式表单步骤:

(1)用代码编写数据模型
(2)将数据模型与HTML页面ngModel相连接

2.数据模型:

(1)数据模型:用来保存表单数据的数据结构,简称模型。由FormsModule中的三个类
  组成:FormControl、FormGroup、FormArray.

FormControl指代form中的单个字段
FormGroup指代form中的所有字段或者有关联的字段,类型为对象,可嵌套
FormArray指代form中可能有多个值的字段,类型为数组

(2)例子:创建响应式表单中,不同类型的数据:
    private formModel: FormGroup;

      constructor() {
        this.formModel = new FormGroup({   // 整个表单是FormGroup类型的数据
        
          nickname: new FormControl(),    // 昵称为FormControl类型的数据
          mobile: new FormControl(),
          
          emails: new FormArray([         // emails 是FormArray类型的数据
            new FormControl('a@a.com'),
            new FormControl('b@b.com')
          ]),
          
          passwordInfo: new FormGroup({    // 密码是FormGroup类型的数据,被嵌套在FormGroup中
            password: new FormControl(),
            passwordConfirm: new FormControl()
          })
      });
    }

(3)注意情况:

  1.响应式表单的指令: 来自ReactiveFormModule模块中
  
  2.属性绑定式指令: 使用属性绑定的方式进行绑定
  formGroup、formControl 
  用法:只有当创建了对应的变量时,才能使用:
private formModel: FormGroup;
  3.name绑定式指令:直接绑定字段的名称即可
  formGroupName、formControlName、formArrayName

  4.响应式表单指令是不能创建本地变量来引用。
  
  5.创建响应式表单数据结构:
      export class ReactiveFormComponent implements OnInit {

  

    private xxx: FormControl;   

    private formModel: FormGroup;               //  声明变量;

    constructor() {                            //创建表单数据结构
        this.formModel = new FormGroup({       // 表单对象
            nickname: new FormControl(),
            mobile: new FormControl(),
            emails: new FormArray([            // 数组
                new FormControl('a@a.com'),
                new FormControl('b@b.com')
            ]),
            passwordInfo: new FormGroup({      //  对象
                password: new FormControl(),
                passwordConfirm: new FormControl()
            })
        });
    }
6.绑定方法
    addEmail() {
        let emails = this.formModel.get('emails') as FormArray;  
        emails.push(new FormControl());
        console.log(this.formModel.value);
    }

    createUser() {
        console.log(this.formModel.value);
    }
7.响应式表单模板
    <input [formControl]="xxx"></input>  //  表单外字段可使用formControl属性方式绑定
    <form [formGroup]="formModel" (submit)="createUser()">    // 绑定表单
      <div>姓名:<input type="text" formControlName="nickname"></div>    // 绑定单个字段名称
      <div>邮箱:
        <ul formArrayName="emails">                                      // 绑定数组类数据
          <li *ngFor="let email of formModel.get('emails').controls;let i = index;">
            <input [formControlName]="i">   // 数组没有key值,且i为变量,所以采用属性绑定写法
          </li>
        </ul>
        <button tyoe="button" (click)="addEmail()">增家email</button>
        <!--<input type="email" formControlName="email">-->
      </div>
      <div>手机号:<input type="number" formControlName="mobile"></div>
      <div formGroupName="passwordInfo">                                     // 绑定相关联的字段组合
        <div>密码:<input type="password"  formControlName="password"></div>
        <div>确认密码:<input type="password"  formControlName="passwordConfirm"></div>
      </div>
      <button tyoe="submit">注册</button>
    </form>
8.响应式表单数据创建简化: FormBuild数据类型
    private formModel: FormGroup;

    private fb: FormBuilder = new FormBuilder();
constructor() {
        this.formModel = this.fb.group({
            nickname: ['hello'],
            mobile: [''],
            emails: this.fb.array([
                ['a@a.com'],
                ['b@b.com']
            ]),
            passwordInfo: this.fb.group({
                password: [''],
                passwordConfirm: ['']
            })
        });
    }
用this.fb.group({})代替new FormGroup({})、
用this.fb.array代替  new FormArray({})、 
用['hello']代替new FormControl()

四.表单校验

默认angular表单校验

nickname: ['xxx', [Validators.required, Validators.minLength(6)]],

// 提交时打印校验结果

let nicknameValid: boolean = this.formModel.get('nickname').valid;
    console.log(nicknameValid);
    let nicknamErrors: any = this.formModel.get('nickname').errors;
    console.log(nicknamErrors);

自定义表单校验

  mobileValidator(mobile: FormControl): any {
    let value = (mobile.value || '') + '';
    let myreg = /^(((15[0-9]{1})|(13[0-9]{1})|(18[0-9]{1}))+\d{8})$/;
    let valid = myreg.test(value);
    console.log(valid);
    return valid? null: {mobile: true}; 
  }

  passwordValidator(info: FormGroup): any {
    let password: FormControl = info.get('password') as FormControl;   // 获取其值
    let pConrfirm: FormControl = info.get('passwordConfirm') as FormControl;
    let valid = password.value === pConrfirm.value;
    console.log(valid);
    return valid? null: {password: true};
  }
  
  
  // 使用:
  mobile: ['', [this.mobileValidator]],  // FormControl数据模型格式为数组格式
  
  passwordInfo: this.fb.group({   
    password: [''],
    passwordConfirm: ['']
  }, {validator: this.passwordValidator})  // FormGroup对象格式

整个表单的校验

单个数据有自己的校验,整个表单数据也有一个整体的校验:

this.formModel.valid

当返回true时,证明整个表单都验证合法。

添加校验提示

1. 模板添加校验提示

<div [hidden]="!formModel.hasError('required','nickname')" class="error">
    昵称是必填项
</div>
<div [hidden]="!formModel.hasError('mobile','mobile')" class="error">
    手机号格式不正确
 </div>

formModel.hasError 方法传入两个参数,第一个为验证条件,第二个为数据名称


当数据为嵌套中的数据时,校验规则为:(注意数据传入方式)

<div [hidden]="!formModel.hasError('required','passwordInfo.password')" class="error">
    密码是必填项
</div>

2. 自定义的校验方法返回校验提示:

- 自定义校验方法:
export function passwordValidator(info: FormGroup): any {
  let password: FormControl = info.get('password') as FormControl;
  let pConrfirm: FormControl = info.get('passwordConfirm') as FormControl;
  let valid: boolean = password.value === pConrfirm.value;
  console.log(valid); 
  return valid ? null : {password: {description: '密码与确认密码不匹配'}};  // 返回一个对象
}    

- 展示校验内容:
<div [hidden]="!formModel.hasError('password','passwordInfo')" class="error">
    {{formModel.getError('password','passwordInfo')?.description}}
  </div>

3. 添加异步校验:

- 自定义校验方法:

export function mobileAsyncValidator(mobile: FormControl): any {
  let value = (mobile.value || '') + '';
  let myreg = /^(((15[0-9]{1})|(13[0-9]{1})|(18[0-9]{1}))+\d{8})$/;
  let valid = myreg.test(value);
  console.log(valid);
  **return Observable.of(valid ? null : {mobile: true}).delay(5000);**  
}

- 引用:
.ts文件:
mobile: ['', mobileValidator, mobileAsyncValidator]
传入的三个参数: 默认值,本地校验,异步校验
当本地校验通过后,才进行异步校验

- 模板展示:
<div>
  {{formModel.status}}
</div>

五.状态字段(辅助数据校验)

状态字段

1. touched 和 untouched

<div [hidden]="formModel.get('passwordInfo.password').valid || formModel.get('passwordInfo.password').untouched">
      <div [hidden]="!formModel.hasError('required','passwordInfo.password')" class="error">
        密码是必填项
      </div>
</div>

当数据验证合法或者没有被触碰时,隐藏提示内容

2. pristine 和 dirty

数据是否发生改变

<div [hidden]="formModel.get('mobile').valid || formModel.get('mobile').pristine">
    <div [hidden]="!formModel.hasError('mobile','mobile')" class="error">
      手机号格式不正确
    </div>
</div>

当数据合法或者没有被触碰时,隐藏提示内容

3. pending

是否进行远程异步校验时

<div [hidden]="!formModel.get('mobile').pending">
    正在校验手机号合法性
</div>

动态添加类名:

[class.hasError]="formModel.get('nickname').invalid"

模板式表单的验证提示:

自定义指令:

  1. 建立指令文件的 指令: ng g directive 指令名

  2. 校验指令文件:

import {Directive} from '@angular/core';
import {NG_VALIDATORS} from "@angular/forms";
import {mobileValidator} from "../validators/Validators";

@Directive({
  selector: '[mobile]',  // 指令名称,可编辑
  providers: [{provide: NG_VALIDATORS, useValue: mobileValidator, multi: true}]
  // NG_VALIDATORS 校验所用模块,mobileValidator:校验方法
})
export class MobileValidatorDirective {

  constructor() {
  }

}

  1. 模板使用angular默认校验指令的校验提示:
// 手机号验证
<div [hidden]="!myForm.form.hasError('mobile','mobile')" class="error">
    手机号格式不正确
</div>

// 密码验证:

<div [hidden]="!myForm.form.hasError('required','passwordInfo.password')" class="error">
    密码是必填项
</div>
<div [hidden]="!myForm.form.hasError('password','passwordInfo')" class="error">
    {{myForm.form.getError('password','passwordInfo')?.description}}
</div>  // 其中.hasError(),.getError(),中的传参第一个参数是印证错误时,返回的错误的字段。

与响应式不同的是: 不能使用表单数据模型,只能使用myForm.form代替

  1. 模板式表单不能使用状态值(touched 、pristine....),需要使用其他方法代替:

绑定input方法

<div>手机号: <input type="number" ngModel mobile name="mobile" (input)="onMobileInpue(myForm)"></div>

onMobileInpue(form: NgForm) {
    if (form) {
      this.mobileValid = form.form.get('mobile').valid;
      this.mobilePristine = form.form.get('mobile').pristine;

    }
}

间接获取 状态值pristine

<div [hidden]="mobileValid || mobilePristine">
    <div [hidden]="!myForm.form.hasError('mobile','mobile')" class="error">
      手机号格式不正确
    </div>
</div>

综上: 模板式表单如果需要更具体的数据检测,则会更加麻烦,
所以模板式表单只是个简单的表单创建.


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容