• 技术文章 >web前端 >js教程

    深入了解angular中的表单(响应式和模板驱动)

    青灯夜游青灯夜游2022-05-25 21:14:20转载473
    本篇文章带大家了解一下angular中的表单,聊聊响应式表单与模板驱动表单,介绍一下响应式表单怎么验证表单输入,希望对大家有所帮助!

    一、angular表单简介

    Angular 提供了两种不同的方法来通过表单处理用户输入:响应式表单模板驱动表单。 两者都从视图中捕获用户输入事件、验证用户输入、创建表单模型、修改数据模型,并提供跟踪这些更改的途径。【相关教程推荐:《angular教程》】

    1.1 响应式表单与模板驱动表单的差异


    响应式模板驱动
    建立表单模型显式的,在组件类中创建隐式的,由指令创建
    数据模型结构化和不可变的非结构化和可变的
    可预测性同步异步
    表单验证函数指令

    1.2 建立表单模型

    响应式表单和模板驱动型表单都会跟踪用户与之交互的表单输入元素和组件模型中的表单数据之间的值变更。这两种方法共享同一套底层构建块,只在如何创建管理常用表单控件实例方面有所不同。

    1.3 常用表单基础类

    响应式表单和模板驱动表单都建立在下列基础类之上。

    二、 响应式表单

    响应式表单使用显式的、不可变的方式,管理表单在特定的时间点上的状态。对表单状态的每一次变更都会返回一个新的状态,这样可以在变化时维护模型的整体性。响应式表单是围绕 Observable 流构建的,表单的输入和值都是通过这些输入值组成的流来提供的,它可以同步访问。

    2.1 添加基础表单控件

    使用表单控件有三个步骤。

    要使用响应式表单控件,就要从 @angular/forms 包中导入 ReactiveFormsModule,并把它添加到你的 NgModule 的 imports 数组中。

    import { ReactiveFormsModule } from '@angular/forms';
    
    @NgModule({
      imports: [
        // other imports ...
        ReactiveFormsModule
      ],
    })
    export class AppModule { }

    要注册一个表单控件,就要导入 FormControl 类并创建一个 FormControl 的新实例,将其保存为类的属性。

    import { Component } from '@angular/core';
    import { FormControl } from '@angular/forms';
    
    @Component({
      selector: 'app-name-editor',
      templateUrl: './name-editor.component.html',
      styleUrls: ['./name-editor.component.css']
    })
    export class NameEditorComponent {
      name = new FormControl('');
    }

    可以用 FormControl 的构造函数设置初始值,这个例子中它是空字符串。通过在你的组件类中创建这些控件,你可以直接对表单控件的状态进行监听修改校验

    在组件类中创建了控件之后,你还要把它和模板中的一个表单控件关联起来。修改模板,为表单控件添加 formControl 绑定,formControl 是由 ReactiveFormsModule 中的 FormControlDirective 提供的。

    <label>
      Name:
      <input type="text" [formControl]="name">
    </label>

    2.2 显示表单控件的值

    你可以用下列方式显示它的值:

    <label>
      Name:
      <input type="text" [formControl]="name">
    </label>
    <p>Value: {{ name.value }}</p>
      public name = new FormControl('test');
    
      public testValueChange() {
        this.name.valueChanges.subscribe({
          next: value => {
            console.log("name value is: " + value);
          }
        })
      }

    2.3 替换表单控件的值

    响应式表单还有一些方法可以用编程的方式``修改控件的值,它让你可以灵活的修改控件的值而不需要借助用户交互。FormControl 提供了一个 setValue() 方法,它会修改这个表单控件的值,并且验证与控件结构相对应的值的结构。比如,当从后端 API 或服务接收到了表单数据时,可以通过 setValue() 方法来把原来的值替换为新的值。

    updateName() {
      this.name.setValue('Nancy' + new Date().getTime());
    }
    <p>
      <button (click)="updateName()">Update Name</button>
    </p>

    2.4 把表单控件分组

    表单中通常会包含几个相互关联的控件。响应式表单提供了两种把多个相关控件分组到同一个输入表单中的方法。

    要将表单组添加到此组件中,请执行以下步骤。

    在组件类中创建一个名叫 profileForm 的属性,并设置为 FormGroup 的一个新实例。要初始化这个 FormGroup,请为构造函数提供一个由控件组成的对象,对象中的每个名字都要和表单控件的名字一一对应。

    import { FormControl, FormGroup } from '@angular/forms';
      profileForm = new FormGroup({
        firstName: new FormControl(''),
        lastName: new FormControl(''),
      });
      // 可以整个获取值
      public onSubmit() {
        // TODO: Use EventEmitter with form value
        console.warn(this.profileForm.value);// {firstName: "", lastName: ""}
      }
          // 可以借助 valueChanges 整个可观察对象整个获取值
         this.profileForm.valueChanges.subscribe( {
          next: value => {
            console.log("name value is: " + JSON.stringify(value)); // dashboard.component.ts:53 name value is: {"firstName":"dddd","lastName":"bb"}
          }
        })
    
        // 可以通过后期单个控件单独获取值
        this.profileForm.get('firstName').valueChanges.subscribe({
          next: value => {
            console.log("First Name is: " + value); // First Name is: aa
          }

    ps: 这个 FormGroup 用对象的形式提供了它的模型值,这个值来自组中每个控件的值。 FormGroup 实例拥有和 FormControl 实例相同的属性(比如 value、untouched)和方法(比如 setValue())。

    这个表单组还能跟踪其中每个控件的状态及其变化,所以如果其中的某个控件的状态或值变化了,父控件也会发出一次新的状态变更或值变更事件。该控件组的模型来自它的所有成员。在定义了这个模型之后,你必须更新模板,来把该模型反映到视图中。

    <form [formGroup]="profileForm" (ngSubmit)="onSubmit()">
     
      <label>
        First Name:
        <input type="text" formControlName="firstName">
      </label>
    
      <label>
        Last Name:
        <input type="text" formControlName="lastName">
      </label>
    
      <button type="submit" [disabled]="!profileForm.valid">Submit</button>
    </form>

    2.5 创建嵌套的表单组

    表单组可以同时接受单个表单控件实例和其它表单组实例作为其子控件。这可以让复杂的表单模型更容易维护,并在逻辑上把它们分组到一起。
    要制作更复杂的表单,请遵循如下步骤。

    要在 profileForm 中创建一个嵌套组,就要把一个嵌套的 address 元素添加到此表单组的实例中。

      public profileForm = new FormGroup({
        firstName: new FormControl(''),
        lastName: new FormControl(''),
        address: new FormGroup({
          street: new FormControl(''),
          city: new FormControl(''),
          state: new FormControl(''),
          zip: new FormControl('')
        })
      });
        // 可以借助 valueChanges 整个可观察对象整个获取值
        this.profileForm.valueChanges.subscribe( {
          next: value => {
            console.log("name value is: " + JSON.stringify(value));// name value is: {"firstName":"","lastName":"","address":{"street":"b","city":"","state":"","zip":""}}
          }
        });
    
        // 可以通过后期单个控件单独获取值
        this.profileForm.get('firstName').valueChanges.subscribe({
          next: value => {
            console.log("First Name is: " + value);
          }
        });
    
        // 可以获取form组件某个form组的整个值
        this.profileForm.get('address').valueChanges.subscribe(({
          next: value => {
            console.log('address value is: ' + JSON.stringify(value));// address value is: {"street":"b","city":"","state":"","zip":""}
          }
        }));
    
        // 可以获取form组件某个form组的某个formcontrol实例的值
        this.profileForm.get('address').get('street').valueChanges.subscribe(({
          next: value => {
            console.log('street value is: ' + value);// street value is: b
          }
        }));

    在修改了组件类中的模型之后,还要修改模板,来把这个 FormGroup 实例对接到它的输入元素。

          <form [formGroup]="profileForm" (ngSubmit)="onSubmit()">
    
            <label>
              First Name:
              <input type="text" formControlName="firstName">
            </label>
    
            <label>
              Last Name:
              <input type="text" formControlName="lastName">
            </label>
            <div formGroupName="address">
              <h3>Address</h3>
    
              <label>
                Street:
                <input type="text" formControlName="street">
              </label>
    
              <label>
                City:
                <input type="text" formControlName="city">
              </label>
    
              <label>
                State:
                <input type="text" formControlName="state">
              </label>
    
              <label>
                Zip Code:
                <input type="text" formControlName="zip">
              </label>
            </div>
            <button type="submit" [disabled]="!profileForm.valid">Submit</button>
          </form>

    2.6 更新部分数据模型

    当修改包含多个 FormGroup 实例的值时,你可能只希望更新模型中的一部分,而不是完全替换掉。

    有两种更新模型值的方式:

    setValue() 方法的严格检查可以帮助你捕获复杂表单嵌套中的错误,而 patchValue() 在遇到那些错误时可能会默默的失败。

      public updateProfile() {
          // profileForm 模型中只有 firstName 和 street 被修改了。注意,street 是在 address 属性的对象中被修改的。这种结构是必须的,因为 patchValue() 方法要针对模型的结构进行更新。patchValue() 只会更新表单模型中所定义的那些属性。
        this.profileForm.patchValue({
          firstName: 'Nancy' + new Date().getTime(),
          address: {
            street: '123 Drew Street' + new Date().getTime()
          }
        });
    
        // ERROR Error: Must supply a value for form control with name: 'lastName'.
        // setValue() 方法会严格遵循表单组的结构
        this.profileForm.setValue({
          firstName: 'Nancy' + new Date().getTime(),
          address: {
            street: '123 Drew Street' + new Date().getTime()
          }
        });
      }

    2.7 创建动态表单

    FormArray 是 FormGroup 之外的另一个选择,用于管理任意数量的匿名控件。像 FormGroup 实例一样,你也可以往 FormArray 中动态插入和移除控件,并且 FormArray 实例的值和验证状态也是根据它的子控件计算得来的。 不过,你不需要为每个控件定义一个名字作为 key,因此,如果你事先不知道子控件的数量,这就是一个很好的选择。

    要定义一个动态表单,请执行以下步骤。

    通过把一组(从零项到多项)控件定义在一个数组中来初始化一个 FormArray。为 profileForm 添加一个 aliases 属性,把它定义为 FormArray 类型。

    import { FormControl, FormGroup, FormArray } from '@angular/forms';
    
      public profileForm = new FormGroup({
        firstName: new FormControl(''),
        lastName: new FormControl(''),
        address: new FormGroup({
          street: new FormControl(''),
          city: new FormControl(''),
          state: new FormControl(''),
          zip: new FormControl('')
        }),
        aliases: new FormArray([
          new FormControl('1')
        ])
      });
      public aliases = (<FormArray>this.profileForm.get('aliases'));
    
      public addAlias() {
        (<FormArray>this.profileForm.get('aliases')).push(new FormControl('1'));
      }
    
          // 获取整个 formArray 的数据
        this.profileForm.get('aliases').valueChanges.subscribe({
          next: value => {
            console.log('aliases values is: ' + JSON.stringify(value)); // aliases values is: ["1","3"]
          }
        });
    
        // 获取 formArray 中单个 formControl 的数据
        (<FormArray>this.profileForm.get('aliases')).controls[0].valueChanges.subscribe({
          next: value => {
            console.log('aliases[0] values is: ' + value); // aliases[0] values is: 0
          }
        })

    要想为表单模型添加 aliases,你必须把它加入到模板中供用户输入。和 FormGroupNameDirective 提供的 formGroupName 一样,FormArrayNameDirective 也使用 formArrayName 在这个 FormArray 实例和模板之间建立绑定

          <form [formGroup]="profileForm" (ngSubmit)="onSubmit()">
    
            <label>
              First Name:
              <input type="text" formControlName="firstName">
            </label>
    
            <label>
              Last Name:
              <input type="text" formControlName="lastName">
            </label>
            <div formGroupName="address">
              <h3>Address</h3>
    
              <label>
                Street:
                <input type="text" formControlName="street">
              </label>
    
              <label>
                City:
                <input type="text" formControlName="city">
              </label>
    
              <label>
                State:
                <input type="text" formControlName="state">
              </label>
    
              <label>
                Zip Code:
                <input type="text" formControlName="zip">
              </label>
            </div>
            <div formArrayName="aliases">
              <h3>Aliases</h3> <button (click)="addAlias()">Add Alias</button>
    
              <div *ngFor="let alias of aliases.controls; let i=index">
                <!-- The repeated alias template -->
                <label>
                  Alias:
                  <input type="text" [formControlName]="i">
                </label>
              </div>
            </div>
          </form>

    2.8 响应式表单 API 汇总

    说明
    AbstractControl所有三种表单控件类(FormControl、FormGroup 和 FormArray)的抽象基类。它提供了一些公共的行为和属性。
    FormControl管理单体表单控件的值和有效性状态。它对应于 HTML 的表单控件,比如 或 。
    FormGroup管理一组 AbstractControl 实例的值和有效性状态。该组的属性中包括了它的子控件。组件中的顶层表单就是 FormGroup。
    FormArray管理一些 AbstractControl 实例数组的值和有效性状态。
    FormBuilder一个可注入的服务,提供一些用于提供创建控件实例的工厂方法。

    三、模板驱动表单

    在模板驱动表单中,表单模型是隐式的,而不是显式的。指令 NgModel 为指定的表单元素创建并管理一个 FormControl 实例。
    下面的组件使用模板驱动表单为单个控件实现了同样的输入字段。

    import { Component } from '@angular/core';
    
    @Component({
      selector: 'app-template-favorite-color',
      template: `
        Favorite Color: <input type="text" [(ngModel)]="favoriteColor">
      `
    })
    export class FavoriteColorComponent {
      favoriteColor = '';
    }

    四、响应式表单验证表单输入

    在组件类中直接把验证器函数添加到表单控件模型上(FormControl)。然后,一旦控件发生了变化,Angular 就会调用这些函数。

    4.1 验证器(Validator)函数

    验证器函数可以是同步函数,也可以是异步函数。

    出于性能方面的考虑,只有在所有同步验证器都通过之后,Angular 才会运行异步验证器。当每一个异步验证器都执行完之后,才会设置这些验证错误。

    4.2 内置验证器函数

    在模板驱动表单中用作属性的那些内置验证器,比如 required 和 minlength,也都可以作为 Validators 类中的函数使用

     public profileForm = new FormGroup({
        firstName: new FormControl('', [
          Validators.required
        ]),
      });
    
        this.profileForm.get('firstName').valueChanges.subscribe({
          next: value => {
            console.log("First Name is: " + value);
            console.log(this.profileForm.get('firstName').errors);// { required: true } | null
          }
        });
          <form [formGroup]="profileForm">
    
            <label>
              First Name:
              <input type="text" formControlName="firstName">
              <div *ngIf="firstName.errors?.required">
                Name is required.
              </div>
            </label>
        </form>

    4.3 定义自定义验证器

    内置的验证器并不是总能精确匹配应用中的用例,因此有时你需要创建一个自定义验证器。

      public profileForm = new FormGroup({
        firstName: new FormControl('', [
          Validators.required,
          this.forbiddenNameValidator(/bob/i)
        ])
      });
    
      public forbiddenNameValidator(nameRe: RegExp): ValidatorFn {
        return (control: AbstractControl): {[key: string]: any} | null => {
          const forbidden = nameRe.test(control.value);
          return forbidden ? {forbiddenName: {value: control.value}} : null;
        };
      }
    
      get firstName() { return this.profileForm.get('firstName'); }
    
    
    
          this.profileForm.get('firstName').valueChanges.subscribe({
          next: value => {
            console.log("First Name is: " + value); // First Name is: bob
            console.log(JSON.stringify(this.profileForm.get('firstName').errors));// {"forbiddenName":{"value":"bob"}} | null
          }
        });

    4.4 跨字段交叉验证

    跨字段交叉验证器是一种自定义验证器,可以对表单中不同字段的值进行比较,并针对它们的组合进行接受或拒绝。

    下列交叉验证的例子说明了如何进行如下操作:

    要想在单个自定义验证器中计算这两个控件,你就必须在它们共同的祖先控件中执行验证: FormGroup。你可以在 FormGroup 中查询它的子控件,从而让你能比较它们的值。要想给 FormGroup 添加验证器,就要在创建时把一个新的验证器传给它的第二个参数。

        this.profileForm.valueChanges.subscribe( {
          next: value => {
            console.log(JSON.stringify(this.profileForm.errors));// {"identityRevealed":true} | null
          }
        });
    
      public profileForm = new FormGroup({
        firstName: new FormControl('', [
          Validators.required,
        ]),
        lastName: new FormControl(''),
      }, { validators: this.identityRevealedValidator});
    
      public identityRevealedValidator(control: FormGroup): ValidationErrors | null{
        const firstName = control.get('firstName');
        const lastName = control.get('lastName');
        return firstName && lastName && firstName.value === lastName.value ? { identityRevealed: true } : null;
      };

    4.5 创建异步验证器

    异步验证器实现了 AsyncValidatorFnAsyncValidator 接口。它们与其同步版本非常相似,但有以下不同之处。

    异步验证在同步验证完成后才会发生,并且只有在同步验证成功时才会执行。如果更基本的验证方法已经发现了无效输入,那么这种检查顺序就可以让表单避免使用昂贵的异步验证流程(例如 HTTP 请求)。

    4.6 触发某个formControlName

        let formControl = this.profileForm.get('firstName');
        formControl.updateValueAndValidity();

    更多编程相关知识,请访问:编程视频!!

    以上就是深入了解angular中的表单(响应式和模板驱动)的详细内容,更多请关注php中文网其它相关文章!

    声明:本文转载于:csdn,如有侵犯,请联系admin@php.cn删除
    专题推荐:Angular Angular.js
    上一篇:一文搞懂JavaScript WebAPI 下一篇:一文浅析angular中的组件模板
    VIP课程(WEB全栈开发)

    相关文章推荐

    • 【腾讯云】年中优惠,「专享618元」优惠券!• Angular中怎么自定义视频播放器• 浅析Angular+rxjs怎么实现拖拽功能?• 什么是管道?浅析Angular中的管道(PIPE)• 一文聊聊Angular中的生命周期• Angular项目如何上线?结合nginx来聊聊上线流程!• Angular项目中怎么使用 SASS 样式
    1/1

    PHP中文网