angular表单

表单

angular 中提供了两种表单形式:响应式表单和模板驱动表单。

理论准备

选择适合你的表单形式:

  • 响应式表单提供对底层表单对象模型直接、显式的访问。它们与模板驱动表单相比,更加健壮:它们的可扩展性、可复用性和可测试性都更高。适用于比较复杂的表单。
  • 模板驱动表单依赖模板中的指令来创建和操作底层的对象模型。它们很容易添加到应用中,但在扩展性方面不如响应式表单。适用于简单的表单。

无论哪种形式的表单,都会有下面4个常用基础类:

  • FormControl实例用于追踪单个表单控件的值和验证状态;
  • FormGroup用于追踪一个表单控件组的值和状态;
  • FormArray用于追踪表单控件数组的值和状态;
  • ControlValueAccessor用于在 AngularFormControl实例和原生 DOM 元素之间创建一个桥梁。

响应式表单

按照实际开发的操作程序,我们会单独创建一个模块来演示:

ng g m forms-study/forms-study --flat

将模块引入到你想要放的模块中。

再创建一个今天练习的组件并调用:

ng g c forms-study/reactive-forms -s -c OnPush

一切准备就绪后,我们来创建一个最简单的响应式表单:

  1. 注册响应式表单模块,该模块声明了一些用在响应式表单中的指令:
// forms-study.module.ts
import { ReactiveFormsModule } from '@angular/forms';

@NgModule({
  imports: [
    // ...
    ReactiveFormsModule
  ],
})
  1. 生成一个 FormControl实例,并把它保存在组件中:
// reactive-forms.component.ts
export class ReactiveFormsComponent implements OnInit {
  name = new FormControl(''); // 默认值为''
}

3.在模板中使用 FormControl:

<!-- reactive-forms.component.html -->
<label>
  Name: <input type="text" [formControl]="name">
</label>

这样,我们就创建了一个最简单的响应式表单。

使用 value获取表单的值:

<p>value: {{name.value}}</p>

在这里插入图片描述

表单分组

FormGroup的实例跟踪一组 FormControl实例(比如一个表单)的表单状态。当创建 FormGroup时,其中的每个控件都会根据其名字进行跟踪。

使用步骤跟上面类似:

  1. 创建一个 FormGroup实例:
// reactive-forms.component.ts
export class ReactiveFormsComponent implements OnInit {
  profileForm = new FormGroup({
    firstName: new FormControl(''),
    lastName: new FormControl(''),
  });
}
  1. 把这个 FormGroup模型关联到视图。
<!-- reactive-forms.component.html -->
<form [formGroup]="profileForm" (ngSubmit)="onSubmit()">
  <div class="form-group">
    <label>
      First Name: <input type="text" formControlName="firstName" class="form-control form-control-sm">
    </label>
  </div>
  <div class="form-group">
    <label>
      Last Name: <input type="text" formControlName="lastName" class="form-control form-control-sm">
    </label>
  </div>
  <button class="btn btn-primary btn-sm"  type="submit">提交</button>
</form>
  1. 保存表单数据。
// reactive-forms.component.ts
onSubmit(): void {
  console.log(this.profileForm.value); // {firstName: "abc", lastName: "def"}
}
嵌套表单组

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

比如我们增加一个 address信息:

// reactive-forms.component.ts
export class ReactiveFormsComponent implements OnInit {
  profileForm = new FormGroup({
    firstName: new FormControl(''),
    lastName: new FormControl(''),
    address: new FormGroup({ // 新增,并设置默认值
      street: new FormControl('建设路32号'),
      city: new FormControl('成都市')
    })
  });
}

当我们使用嵌套的表单组时,我们模板对应的 html代码也应该具有相同的结构,并且设置 FormGroupName值为表单组名:

<!-- reactive-forms.component.html -->
<!-- 其他代码 -->
<div formGroupName="address">
  <div class="form-group">
    <label>
      street: <input type="text" formControlName="street" class="form-control form-control-sm">
    </label>
  </div>
  <div class="form-group">
    <label>
      city: <input type="text" formControlName="city" class="form-control form-control-sm">
    </label>
  </div>
</div>

tips:如果不提供 FormGroupName参数,在下面的 formControlName也不能使用 address.street

获取到的数据也是嵌套形式:

在这里插入图片描述

更新表单数据

更新表单数据有两种方式:

  • 使用 setValue()方法来为单个控件设置新值。 setValue()方法会严格遵循表单组的结构,并整体性替换控件的值。
  • 使用 patchValue()方法可以用对象中所定义的任何属性为表单模型进行替换。

我们添加两个方法来演示 setValuepatchValue两个方法来修改 lastName

// reactive-forms.component.ts
onSetValue(): void {
  this.profileForm.setValue({
    address: {
      city: '北京市'
    }
  });
}
onPatchValue(): void {
  this.profileForm.patchValue({
    address: {
      city: '北京市'
    }
  });
}

patchValue()方法修改成功,而 setValue()方法则报错了:

在这里插入图片描述

因为 setValue()方法严格遵循表单组的结构,所以需要将所有的表单控件的值都加上才能修改成功。

得出结论:修改表单部分值的时候使用 patchValue()方法,否则使用 setValue()方法。

使用 FormBuilder 服务生成控件

当表单中有很多控件时,为每个控件实例化 FormControl显得很麻烦。FormBuilder服务提供了一些便捷方法来生成表单控件。

class FormBuilder {
  group(controlsConfig: { [key: string]: any; }, options: AbstractControlOptions | { [key: string]: any; } = null): FormGroup
  control(formState: any, validatorOrOpts?: ValidatorFn | AbstractControlOptions | ValidatorFn[], asyncValidator?: AsyncValidatorFn | AsyncValidatorFn[]): FormControl
  array(controlsConfig: any[], validatorOrOpts?: ValidatorFn | AbstractControlOptions | ValidatorFn[], asyncValidator?: AsyncValidatorFn | AsyncValidatorFn[]): FormArray
}

FormBuilder有三个方法:

  • group()构建一个新的 FormGroup实例。
  • control()构建一个新的 FormControl实例。
  • array()构造一个新的 FormArray实例。

使用 FormBuildergroup()方法修改上面的表单创建形式:

// reactive-forms.component.ts
// 注入服务
constructor(private fb: FormBuilder) { }
// 构建表单
profileForm = this.fb.group({
  firstName: [''],
  lastName: [''],
  address: this.fb.group({
    street: ['建设路32号'],
    city: ['成都市']
  })
});

这样创建是不是清爽很多呢?效果完全一样。

创建动态表单

有的时候,我们不知道要创建的表单具体有多少个。比如给一个商品增加 size属性,我们根本不知道它有几个尺寸,所以就需要动态创建表单。

使用 FormBuilder.array()方法来定义 FormArray实例数组:

// reactive-forms.component.ts
profileForm = this.fb.group({
  ...
  sizes: this.fb.array([
    this.fb.control('')
  ])
});

使用 getter语法创建类属性 sizes,以便我们在模板中使用:

// reactive-forms.component.ts
import {FormArray} from '@angular/forms';
get sizes() {
  return this.profileForm.get('sizes') as FormArray; // 断言为FormArray类型
}

自定义 addSize()方法,用 FormArray.push()方法为 sizes控件添加新属性:

// reactive-forms.component.ts
import {FormArray} from '@angular/forms';
addSize(): void {
  this.sizes.push(this.fb.control(''));
}

模板中渲染 sizes:

<!-- reactive-forms.component.html -->
<!-- 其他代码 -->
<div formArrayName="sizes">
  <div class="form-group" *ngFor="let size of sizes.controls; let i = index">
    <label>
      Size:
      <input type="text" [formControlName]="i" class="form-control form-control-sm">
    </label>
  </div>
</div>

tips:

  • FormGroup一样,也需要先提供一个 formArrayName属性;
  • 通过 sizes.controls获取添加的动态控件。

在这里插入图片描述

获取数据是一个数组的形式:

在这里插入图片描述

模板驱动表单

我们也将创建一个 template-forms组件来演示:

ng g c forms-study/template-forms -s -c OnPush

所谓模板驱动表单,通俗的讲就是利用 ngModel等指令实现的表单。

要使用 ngModel指令,必须要做的第一件事就是引入 FormsModule

// forms-study.module.ts
@NgModule({
  imports: [
    FormsModule,
    ...
  ]
})

接下来,我们将通过模板驱动表单的形式创建前面的表单。

  1. 构建数据

为了使我们的数据有一个严谨的结构,我们使用一个类来定义数据格式:

// template-forms.component.ts
export class Person {
  constructor(
    public firstName: string,
    public lastName: string,
    public street: string,
    public city: string,
  ) {
  }
}

实例化数据并设置默认值:

// template-forms.component.ts
export class TemplateFormsComponent implements OnInit {
  model = new Person('', '', '建设路32号', '成都市');
}
  1. 在模板中绑定数据:
<!-- template-forms.component.html -->
<form>
  <div class="form-group">
    <label>
      First Name: <input type="text" [(ngModel)]="model.firstName" name="firstName" class="form-control form-control-sm">
    </label>
  </div>
  <div class="form-group">
    <label>
      Last Name: <input type="text" [(ngModel)]="model.lastName" name="lastName" class="form-control form-control-sm">
    </label>
  </div>
  <div class="form-group">
    <label>
      street: <input type="text" [(ngModel)]="model.street" name="street" class="form-control form-control-sm">
    </label>
  </div>
  <div class="form-group">
    <label>
      city: <input type="text" [(ngModel)]="model.city" name="city" class="form-control form-control-sm">
    </label>
  </div>
  <div class="btn-group">
    <button class="btn btn-primary btn-sm" type="submit">提交</button>
  </div>
</form>

注意:如果要在 <form>标签中使用 ngModel指令,应该提供 name属性(详见2.3 双向绑定)。这就相当于响应式模板中提供的 formControlName属性。

在这里插入图片描述

这样,我们实现了数据的双向绑定,也仅仅是双向绑定。如果想用表单的方法,就跟上面一样,还需要在 <form>标签上提供一个类似 FormGroup的属性:

<!-- template-forms.component.html -->
<form #profileForm="ngForm">
  ...
</form>

如果引入了 FormsModule, <form>标签上就可以使用 ngForm指令。 这里的 profileForm就是 ngForm类型, profileForm.form就是 FormGroup实例。

将表单自己作为参数传入:

<!-- template-forms.component.html -->
<form #profileForm="ngForm" (ngSubmit)="onSubmit(profileForm)">
  ...
</form>

通过 FormGroup实例拿到表单数据:

// template-forms.component.ts
onSubmit(f: NgForm): void {
  console.log(f.form.value); // {firstName: "aa", lastName: "bb", street: "建设路32号", city: "成都市"}
}

使用 FormGroupreset()方法重置表单:

// template-forms.component.ts
resetForm(f: NgForm): void {
  // reset(value: any = {}, options: { onlySelf?: boolean; emitEvent?: boolean; } = {}): void
  
  f.reset({firstName: 'Jack', lastName: 'Bob'});
}

在这里插入图片描述

跟踪控件状态

Angular会跟踪表单组件的状态,会告诉你用户是否接触过该控件、该值是否发生了变化,或者该值是否无效。并且会在控件上以特殊的 CSS类来反映其状态。

在这里插入图片描述

在这里插入图片描述

我们可以用这些 CSS类来根据控件的状态定义其样式。这也将为后面我们介绍表单验证做一点准备。

总结

  1. 响应式表单主要是通过操作表单对象来定义表单,用于较复杂结构的表单,模版驱动表单则相反;
  2. 修改表单部分值的时候使用 patchValue()方法,否则使用 setValue()方法;
  3. 使用 FormArray.push()方法可以创建动态表单;
  4. 可以根据 angular跟踪的表单状态来定制样式。

欢迎关注我的公众号,公众号将第一时间更新angular教程:
在这里插入图片描述

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Angular 验证是一种在 Web 应用程序中对用户输入进行验证的方法。它可以防止用户在提交之前输入不正确的数据。在 Angular 中,验证是通过构建验证器函数并将其应用于控件来完成的。以下是一个简的例子,演示如何在 Angular 中应用验证: 1. 在 HTML 模板中定义: ```html <form (ngSubmit)="onSubmit()" #myForm="ngForm"> <label> Name: <input type="text" name="name" ngModel required> </label> <label> Email: <input type="email" name="email" ngModel required email> </label> <button type="submit" [disabled]="!myForm.form.valid">Submit</button> </form> ``` 2. 在组件中定义验证器函数: ```typescript import { Component } from '@angular/core'; import { FormGroup, FormBuilder, Validators } from '@angular/forms'; @Component({ selector: 'app-form', templateUrl: './form.component.html', styleUrls: ['./form.component.css'] }) export class FormComponent { myForm: FormGroup; constructor(private fb: FormBuilder) { this.myForm = this.fb.group({ name: ['', Validators.required], email: ['', [Validators.required, Validators.email]] }); } onSubmit() { console.log(this.myForm.value); } } ``` 在上面的代码中,我们使用 `FormBuilder` 创建了一个名为 `myForm` 的,并添加了两个控件:`name` 和 `email`。`name` 控件是必填的,而 `email` 控件则需要符合电子邮件格式。在 `onSubmit` 方法中,我们可以访问的值,并进行进一步的处理。 值得注意的是,为了让控件与模板中的 `ngModel` 指令进行绑定,我们需要在组件中导入 `FormsModule` 模块。在模板中,我们还使用了 Angular 的模板语法,如 `ngSubmit` 事件和 `[disabled]` 属性,来控制的行为。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yanyi24

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值