首頁 > web前端 > js教程 > 深入了解angular中的表單(響應式和範本驅動)

深入了解angular中的表單(響應式和範本驅動)

青灯夜游
發布: 2022-05-26 21:17:17
轉載
3199 人瀏覽過

這篇文章帶大家了解一下angular中的表單,聊聊響應式表單與範本驅動表單,介紹一下響應式表單怎麼驗證表單輸入,希望對大家有幫助!

深入了解angular中的表單(響應式和範本驅動)

一、angular表單簡介

#Angular 提供了兩種不同的方法來透過表單處理使用者輸入:響應式表單範本驅動表單。兩者都從視圖中捕獲用戶輸入事件、驗證用戶輸入、建立表單模型、修改資料模型,並提供追蹤這些變更的途徑。 【相關教學推薦:《angular教學》】

1.1 響應式表單與範本驅動表單的差異

  • 響應式表單提供對底層表單物件模型直接明確的存取。它們與範本驅動表單相比,更加健壯:它們的可擴展性、可重複使用性和可測試性都更高。如果表單是你的應用程式的關鍵部分,或者你已經在使用響應式表單來建立應用,那就使用響應式表單。
  • 範本驅動表單依賴範本中的指令來建立和操作底層的物件模型。它們對於為應用程式添加一個簡單的表單非常有用,例如電子郵件清單註冊表單。它們很容易添加到應用程式中,但在擴展性方面不如響應式表單。如果你有可以只在範本中管理的非常基本的表單需求和邏輯,那麼範本驅動表單就很合適。
##同步#非同步表單驗證函數#指令

回應式 範本驅動程式
建立表單模型 明確的,在元件類別中建立 隱含的,由指令建立
資料模型 結構化和不可變的 非結構化和可變的
#可預測性
#########指令############

1.2 建立表單模型

響應式表單和範本驅動表單都會追蹤使用者與之互動的表單輸入元素和元件模型中的表單資料之間的值變更。這兩種方法共享同一套底層建構塊,只在如何建立管理常用表單控制項實例方面有所不同。

1.3 常用表單基礎類別

響應式表單和範本驅動表單都建立在下列基礎類別之上。

  • FormControl 實例用於追蹤單一表單控制項的值和驗證狀態。
  • FormGroup 用來追蹤一個表單控制項群組的值和狀態。
  • FormArray 用於追蹤表單控制項陣列的值和狀態。
  • ControlValueAccessor 用於在 Angular 的 FormControl 實例和原生 DOM 元素之間建立一個橋樑。

二、 反應式表單

回應式表單使用明確的、不可變的方式,管理表單在特定的時間點上的狀態。表單狀態的每一次變更都會回到一個新的狀態,這樣可以在變更時維護模型的整體性。響應式表單是圍繞著 Observable 流構建的,表單的輸入和值都是透過這些輸入值組成的流來提供的,它可以同步存取。

2.1 新增基礎表單控制項

使用表單控制項有三個步驟。

  • 在你的應用程式中註冊響應式表單模組。這個模組聲明了一些你要用在響應式表單中的指令。

  • 產生一個新的 FormControl 實例,並且把它保存在元件中。

  • 在範本中註冊這個 FormControl。

要使用響應式表單控件,就要從 @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 顯示表單控制項的值

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

  • 透過可觀察物件valueChanges,你可以在範本中使用AsyncPipe 或在元件類別中使用subscribe() 方法來監聽表單值的變化。
  • 使用 value 屬性。它能讓你獲得目前價值的一份快照。
<label>
  Name:
  <input type="text" [formControl]="name">
</label>

Value: {{ name.value }}

登入後複製
  public name = new FormControl(&#39;test&#39;);

  public testValueChange() {
    this.name.valueChanges.subscribe({
      next: value => {
        console.log("name value is: " + value);
      }
    })
  }
登入後複製

2.3 取代表單控制項的值

#響應式表單還有一些方法可以用程式設計的方式``修改#控制項的值,它讓你可以靈活的修改控制項的值而不需要藉助使用者互動。 FormControl 提供了一個 setValue() 方法,它會修改這個表單控制項的值,並且驗證與控制項結構相對應的值的結構。例如,當從後端 API 或服務接收到了表單資料時,可以透過 setValue() 方法來把原來的值替換為新的值。

updateName() {
  this.name.setValue(&#39;Nancy&#39; + new Date().getTime());
}
登入後複製
<p>
  <button (click)="updateName()">Update Name</button>
</p>
登入後複製

2.4 把表單控制項分組

#表單中通常會包含幾個相互關聯的控制項。響應式表單提供了兩種把多個相關控制項分組到同一個輸入表單中的方法。

  • 表單組定義了一個有一組控制項的表單,你可以把它們放在一起管理。表單組的基礎知識將在本節中討論。你也可以透過巢狀表單群組來建立更複雜的表單。
  • 表單陣列定義了一個動態表單,你可以在執行時新增和刪除控制項。你也可以透過巢狀表單陣列來建立更複雜的表單

要將表單群組新增到此元件中,請執行下列步驟。

  • 建立一個 FormGroup 實例。

  • 把這個 FormGroup 模型關聯到視圖。

  • 儲存表單資料。

在元件類別中建立一個名叫 profileForm 的屬性,並設定為 FormGroup 的一個新實例。要初始化這個 FormGroup,請為建構函式提供一個由控制項組成的對象,而物件中的每個名字都要和表單控制項的名字一一對應。

import { FormControl, FormGroup } from &#39;@angular/forms&#39;;
  profileForm = new FormGroup({
    firstName: new FormControl(&#39;&#39;),
    lastName: new FormControl(&#39;&#39;),
  });
  // 可以整个获取值
  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(&#39;firstName&#39;).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(&#39;&#39;),
    lastName: new FormControl(&#39;&#39;),
    address: new FormGroup({
      street: new FormControl(&#39;&#39;),
      city: new FormControl(&#39;&#39;),
      state: new FormControl(&#39;&#39;),
      zip: new FormControl(&#39;&#39;)
    })
  });
    // 可以借助 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(&#39;firstName&#39;).valueChanges.subscribe({
      next: value => {
        console.log("First Name is: " + value);
      }
    });

    // 可以获取form组件某个form组的整个值
    this.profileForm.get(&#39;address&#39;).valueChanges.subscribe(({
      next: value => {
        console.log(&#39;address value is: &#39; + JSON.stringify(value));// address value is: {"street":"b","city":"","state":"","zip":""}
      }
    }));

    // 可以获取form组件某个form组的某个formcontrol实例的值
    this.profileForm.get(&#39;address&#39;).get(&#39;street&#39;).valueChanges.subscribe(({
      next: value => {
        console.log(&#39;street value is: &#39; + 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() 方法来为单个控件设置新值。 setValue() 方法会严格遵循表单组的结构,并整体性替换控件的值
  • 使用 patchValue() 方法可以用对象中所定义的任何属性为表单模型进行替换。

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

  public updateProfile() {
      // profileForm 模型中只有 firstName 和 street 被修改了。注意,street 是在 address 属性的对象中被修改的。这种结构是必须的,因为 patchValue() 方法要针对模型的结构进行更新。patchValue() 只会更新表单模型中所定义的那些属性。
    this.profileForm.patchValue({
      firstName: &#39;Nancy&#39; + new Date().getTime(),
      address: {
        street: &#39;123 Drew Street&#39; + new Date().getTime()
      }
    });

    // ERROR Error: Must supply a value for form control with name: &#39;lastName&#39;.
    // setValue() 方法会严格遵循表单组的结构
    this.profileForm.setValue({
      firstName: &#39;Nancy&#39; + new Date().getTime(),
      address: {
        street: &#39;123 Drew Street&#39; + new Date().getTime()
      }
    });
  }
登入後複製

2.7 创建动态表单

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

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

  • 导入 FormArray 类。

  • 定义一个 FormArray 控件。

  • 使用 getter 方法访问 FormArray 控件。

  • 在模板中显示这个表单数组

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

import { FormControl, FormGroup, FormArray } from &#39;@angular/forms&#39;;

  public profileForm = new FormGroup({
    firstName: new FormControl(&#39;&#39;),
    lastName: new FormControl(&#39;&#39;),
    address: new FormGroup({
      street: new FormControl(&#39;&#39;),
      city: new FormControl(&#39;&#39;),
      state: new FormControl(&#39;&#39;),
      zip: new FormControl(&#39;&#39;)
    }),
    aliases: new FormArray([
      new FormControl(&#39;1&#39;)
    ])
  });
  public aliases = (<FormArray>this.profileForm.get(&#39;aliases&#39;));

  public addAlias() {
    (<FormArray>this.profileForm.get(&#39;aliases&#39;)).push(new FormControl(&#39;1&#39;));
  }

      // 获取整个 formArray 的数据
    this.profileForm.get(&#39;aliases&#39;).valueChanges.subscribe({
      next: value => {
        console.log(&#39;aliases values is: &#39; + JSON.stringify(value)); // aliases values is: ["1","3"]
      }
    });

    // 获取 formArray 中单个 formControl 的数据
    (<FormArray>this.profileForm.get(&#39;aliases&#39;)).controls[0].valueChanges.subscribe({
      next: value => {
        console.log(&#39;aliases[0] values is: &#39; + 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 &#39;@angular/core&#39;;

@Component({
  selector: &#39;app-template-favorite-color&#39;,
  template: `
    Favorite Color: <input type="text" [(ngModel)]="favoriteColor">
  `
})
export class FavoriteColorComponent {
  favoriteColor = &#39;&#39;;
}
登入後複製

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

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

4.1 验证器(Validator)函数

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

  • 同步验证器:这些同步函数接受一个控件实例,然后返回一组验证错误或 null。你可以在实例化一个 FormControl 时把它作为构造函数的第二个参数传进去。
  • 异步验证器 :这些异步函数接受一个控件实例并返回一个 Promise 或 Observable,它稍后会发出一组验证错误或 null。在实例化 FormControl 时,可以把它们作为第三个参数传入。

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

4.2 内置验证器函数

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

 public profileForm = new FormGroup({
    firstName: new FormControl(&#39;&#39;, [
      Validators.required
    ]),
  });

    this.profileForm.get(&#39;firstName&#39;).valueChanges.subscribe({
      next: value => {
        console.log("First Name is: " + value);
        console.log(this.profileForm.get(&#39;firstName&#39;).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(&#39;&#39;, [
      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(&#39;firstName&#39;); }



      this.profileForm.get(&#39;firstName&#39;).valueChanges.subscribe({
      next: value => {
        console.log("First Name is: " + value); // First Name is: bob
        console.log(JSON.stringify(this.profileForm.get(&#39;firstName&#39;).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(&#39;&#39;, [
      Validators.required,
    ]),
    lastName: new FormControl(&#39;&#39;),
  }, { validators: this.identityRevealedValidator});

  public identityRevealedValidator(control: FormGroup): ValidationErrors | null{
    const firstName = control.get(&#39;firstName&#39;);
    const lastName = control.get(&#39;lastName&#39;);
    return firstName && lastName && firstName.value === lastName.value ? { identityRevealed: true } : null;
  };
登入後複製

4.5 创建异步验证器

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

  • validate() 函数必须返回一个 Promise 或可观察对象
  • 返回的可观察对象必须是有尽的,这意味着它必须在某个时刻完成(complete)。要把无尽的可观察对象转换成有尽的,可以在管道中加入过滤操作符,比如 first、last、take 或 takeUntil。

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

4.6 触发某个formControlName

    let formControl = this.profileForm.get(&#39;firstName&#39;);
    formControl.updateValueAndValidity();
登入後複製

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

以上是深入了解angular中的表單(響應式和範本驅動)的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:csdn.net
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板