Pemahaman mendalam tentang bentuk dalam sudut (responsif dan didorong templat)

青灯夜游
Lepaskan: 2022-05-26 21:17:17
ke hadapan
3123 orang telah melayarinya

Artikel ini akan membawa anda memahami borang dalam sudut, bercakap tentang borang responsif dan borang dipacu templat, dan memperkenalkan cara mengesahkan input borang dalam borang responsif, saya harap ia akan membantu semua orang !

Pemahaman mendalam tentang bentuk dalam sudut (responsif dan didorong templat)

1 Pengenalan kepada bentuk sudut

Angular menyediakan dua kaedah berbeza untuk memproses input pengguna melalui borang: 响应式表单 dan 模板驱动表单. Kedua-duanya menangkap peristiwa input pengguna daripada paparan, mengesahkan input pengguna, mencipta model borang, mengubah suai model data dan menyediakan cara untuk menjejaki perubahan ini. [Cadangan tutorial berkaitan: "tutorial sudut"]

1.1 Perbezaan antara borang responsif dan borang dipacu templat

  • 响应式表单 Menyediakan akses kepada model objek bentuk asas 直接, 显式. Ia lebih teguh daripada borang dipacu templat: ia lebih boleh dikembangkan, boleh digunakan semula dan 可测试性. Jika borang ialah bahagian penting apl anda, atau anda sudah membina apl anda menggunakan borang reaktif, gunakan borang reaktif.
  • 模板驱动表单 bergantung pada 指令 dalam templat untuk mencipta dan mengendalikan model objek asas. Ia berguna untuk menambahkan borang ringkas pada apl anda, seperti borang pendaftaran senarai e-mel. Ia mudah untuk ditambahkan pada apl, tetapi tidak berskala seperti borang responsif. Borang dipacu templat sesuai jika anda mempunyai keperluan bentuk dan logik yang sangat asas yang boleh diuruskan dalam templat sahaja.

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

1.2 Mewujudkan model borang

Kedua-dua borang responsif dan borang dipacu templat menjejaki perubahan nilai antara elemen input borang yang berinteraksi dengan pengguna dan data borang dalam model komponen. Kedua-dua kaedah ini berkongsi set asas yang sama 构建块 dan hanya berbeza dalam cara 创建 dan 管理 biasa digunakan 表单控件实例.

1.3 Kelas asas bentuk yang biasa digunakan

Borang responsif dan borang dipacu templat kedua-duanya dibina di atas kelas asas berikut.

  • Instance FormControl digunakan untuk menjejak nilai dan status pengesahan satu kawalan borang.
  • FormGroup digunakan untuk menjejaki nilai dan status kumpulan kawalan borang.
  • FormArray digunakan untuk menjejak nilai dan keadaan tatasusunan kawalan borang.
  • ControlValueAccessor digunakan untuk mencipta jambatan antara contoh FormControl Angular dan elemen DOM asli.

2. Borang responsif

Borang responsif menggunakan cara yang jelas dan tidak berubah untuk mengurus keadaan borang pada masa tertentu. Setiap perubahan kepada keadaan bentuk mengembalikan keadaan baharu, dengan itu mengekalkan integriti model apabila ia berubah. Borang reaktif dibina di sekitar aliran Observable Input dan nilai borang disediakan melalui aliran nilai input ini, yang boleh diakses 同步.

2.1 Tambah kawalan borang asas

Terdapat tiga langkah untuk menggunakan kawalan borang.

  • Daftar modul borang responsif dalam apl anda. Modul ini mengisytiharkan beberapa arahan yang anda mahu gunakan dalam bentuk reaktif.

  • Jana tika FormControl baharu dan simpan dalam komponen.

  • Daftarkan FormControl ini dalam templat.

Untuk menggunakan kawalan borang reaktif, import ReactiveFormsModule daripada pakej @angular/forms dan tambahkannya pada tatasusunan import NgModule anda.

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

@NgModule({
  imports: [
    // other imports ...
    ReactiveFormsModule
  ],
})
export class AppModule { }
Salin selepas log masuk

Untuk mendaftarkan kawalan borang, import kelas FormControl dan cipta contoh baharu FormControl, menyimpannya sebagai harta kelas.

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('');
}
Salin selepas log masuk

boleh dilakukan menggunakan pembina FormControl 设置初始值, iaitu 空字符串 dalam contoh ini. Dengan mencipta kawalan ini dalam kelas komponen anda, anda boleh 监听, 修改 dan 校验 secara langsung mengawal keadaan kawalan borang.

Selepas mencipta kawalan dalam kelas komponen, anda perlu mengaitkannya dengan kawalan borang dalam templat. Ubah suai templat dan tambahkan pengikatan formControl pada kawalan borang disediakan oleh FormControlDirective dalam ReactiveFormsModule.

<label>
  Name:
  <input type="text" [formControl]="name">
</label>
Salin selepas log masuk

2.2 Memaparkan nilai kawalan borang

Anda boleh memaparkan nilainya dengan cara berikut:

  • Dengan objek boleh diperhatikan valueChanges, anda boleh menggunakan AsyncPipe dalam templat atau menggunakan kaedah subscribe() dalam kelas komponen untuk mendengar perubahan dalam nilai borang.
  • Gunakan atribut nilai. Ia membolehkan anda mendapatkan gambaran nilai semasa.
<label>
  Name:
  <input type="text" [formControl]="name">
</label>

Value: {{ name.value }}

Salin selepas log masuk
  public name = new FormControl(&#39;test&#39;);

  public testValueChange() {
    this.name.valueChanges.subscribe({
      next: value => {
        console.log("name value is: " + value);
      }
    })
  }
Salin selepas log masuk

2.3 Gantikan nilai kawalan borang

Borang responsif juga mempunyai beberapa kaedah untuk menggunakan nilai 编程的方式``修改 kawalan. Membolehkan anda mengubah suai nilai kawalan secara fleksibel tanpa memerlukan interaksi pengguna. FormControl menyediakan kaedah setValue(), yang akan mengubah suai nilai kawalan borang ini dan mengesahkan struktur nilai yang sepadan dengan struktur kawalan. Contohnya, apabila data borang diterima daripada API atau perkhidmatan bahagian belakang, nilai asal boleh digantikan dengan nilai baharu melalui kaedah setValue().

updateName() {
  this.name.setValue(&#39;Nancy&#39; + new Date().getTime());
}
Salin selepas log masuk
<p>
  <button (click)="updateName()">Update Name</button>
</p>
Salin selepas log masuk

2.4 Kawalan borang kumpulan

Sesuatu borang biasanya mengandungi beberapa 相互关联的控件. Borang reaktif menyediakan dua cara untuk mengumpulkan berbilang kawalan berkaitan ke dalam borang input yang sama.

  • 表单组 mentakrifkan borang dengan set kawalan yang boleh anda uruskan bersama. Asas kumpulan bentuk dibincangkan dalam bahagian ini. Anda juga boleh menggunakan 嵌套表单组 untuk mencipta bentuk yang lebih kompleks.
  • 表单数组 mentakrifkan bentuk dinamik di mana anda boleh menambah dan mengalih keluar kawalan semasa masa jalan. Anda juga boleh membuat borang yang lebih kompleks melalui 嵌套表单数组

Untuk menambah kumpulan borang pada komponen ini, ikut langkah di bawah.

  • Mencipta tika FormGroup.

  • Kaitkan model FormGroup ini dengan paparan.

  • Simpan data borang.

Buat harta yang dipanggil profileForm dalam kelas komponen dan tetapkannya kepada contoh baharu FormGroup. Untuk memulakan FormGroup ini, berikan pembina objek yang terdiri daripada kawalan Setiap nama dalam objek mesti sepadan dengan nama kawalan borang.

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
      }
Salin selepas log masuk

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>
Salin selepas log masuk

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
      }
    }));
Salin selepas log masuk

在修改了组件类中的模型之后,还要修改模板,来把这个 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>
Salin selepas log masuk

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()
      }
    });
  }
Salin selepas log masuk

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
      }
    })
Salin selepas log masuk

要想为表单模型添加 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>
Salin selepas log masuk

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;;
}
Salin selepas log masuk

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

在组件类中直接把验证器函数添加到表单控件模型上(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
      }
    });
Salin selepas log masuk
      <form [formGroup]="profileForm">

        <label>
          First Name:
          <input type="text" formControlName="firstName">
          <div *ngIf="firstName.errors?.required">
            Name is required.
          </div>
        </label>
    </form>
Salin selepas log masuk

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
      }
    });
Salin selepas log masuk

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;
  };
Salin selepas log masuk

4.5 创建异步验证器

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

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

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

4.6 触发某个formControlName

    let formControl = this.profileForm.get(&#39;firstName&#39;);
    formControl.updateValueAndValidity();
Salin selepas log masuk

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

Atas ialah kandungan terperinci Pemahaman mendalam tentang bentuk dalam sudut (responsif dan didorong templat). Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Label berkaitan:
sumber:csdn.net
Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Tutorial Popular
Lagi>
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan
Tentang kita Penafian Sitemap
Laman web PHP Cina:Latihan PHP dalam talian kebajikan awam,Bantu pelajar PHP berkembang dengan cepat!