所谓好记性不如烂笔头,本博客是基于《Angular权威教程》上的内容学习的记录笔记,这里也推荐大家去看此书,写得挺不错的。同时若有侵权问题,请留言联系,会及时删除相关博客信息。
在Web应用中,表单或许是最重要的一部分。在Angular中,Angular为我们提供了:表单控件(FormControl)封装了表单中的输入,并提供一些可供操纵的对象,验证器(validator)让我们能以自己喜欢的任何方式验证表单输入,观察者(observer)让我们能够监听表单的变化,并作出相应的回应。
一、FormControl和FormGroup
FormControl和FormGroup是Angular中两个基础的表单对象。
FormControl代表单一的输入字段,它是Angular表单中的最小单元。FormControl封装了这些字段的值和状态,比如是否有效、是否脏(被修改过)或是否有错误等。
FormControl的创建方式:
let name = new FormControl("Nate");
大多数表单都拥有不止一个字段,因此我们需要以某种方式来管理多个FormControl。假设我们需要检测表单的有效性,如果要遍历这个FormControl数组并检查每一个FormControl是否有效,必然相当繁琐,而FormGroup则可以提供总包接口(wrapper interface)来解决这个问题。
FormGroup的创建方式:
let personInfo = new FormGroup({
firstName: new FormControl("Nate");
lastName: new FormControl("Murray");
zip: new FormControl("09210");
});
FormGroup和FormControl都继承自AbstractControl。这意味着检测上述创建的personInfo的状态或值就像检测单个FormControl那么容易。
注意,当我们试图从FormGroup中获取value时,回收到一个“键值对”结构的对象。它能让我们从表单中一次性获取全部的值而不需要单一遍历FormControl。
二、创建我们的第一个表单
创建一个简单的表单:只有一个(带label)输入框和提交按钮。
先把表单变为组件。定义组件需要包含的三部分:
1⃣️配置@Component()注解
2⃣️创建模版
3⃣️在组件定义类中实现自定义功能
为了使用表单,我们需要导入表单库,Angular中有两种使用表单的方式:使用FormsModule和ReactiveFormsModule。我们将这两个库都导入项目中。
在项目的app.ts中导入如下数据:
import { FormsModule, ReactiveFormsModule} from '@angular/forms';
imports: [
FormsModule,
ReactiveFormsModule
],
FormsModule为我们提供了一些模版驱动指令,例如:
1⃣️ngForm
2⃣️ngModel
ReactiveFormsModule则提供了如下指令,例如:
1⃣️formControl
2⃣️ngFormGroup
除此之外还有更多指令,后续逐步介绍。
完整的.ts文件如下:
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule, ReactiveFormsModule} from '@angular/forms';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { DemoFormSkuComponent } from './demo-form-sku/demo-form-sku.component';
@NgModule({
declarations: [
AppComponent,
DemoFormSkuComponent
],
imports: [
BrowserModule,
FormsModule,
ReactiveFormsModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
在HTML文件中定义一个Form表单,代码如下:
<div>
<h2>Demo Form: Sku</h2>
<form #f = "ngForm" (ngSubmit) = "onSubmit(f.value)" class="ui form">
<div class="field">
<label for="skuinput">SKU</label>
<input type="text" name="sku" ngModel id="skuinput" placeholder="SKU">
</div>
<button type="submit" class="ui button">Submit</button>
</form>
</div>
(1)form和NgForm
当我们导入FormsModule时,就可以在视图中使用ngForm指令了。记住,当这些指令在视图中使用时,他就会被附加在任何能匹配其selector的节点上。
在使用ngForm指令时,其做了一件便利但隐晦的工作:它的选择器包含form标签(而不是显示添加ngFrom属性)。这意味着当我们导入FromsModule时,ngFrom就会被自带附加到视图中所有的<form>标签上。这确实非常又有,但由于它发生幕后,也许会让许多人感到困惑。
ngForm为我们提供了两个重要的功能:
1⃣️一个名叫ngForm的FormGroup对象
2⃣️一个输出事件(ngSubmit)
在上面例子<form>标签使用了以上两个功能:
<form #f = "ngForm" (ngSubmit) = "onSubmit(f.value)"></form>
说明:我们使用了#f = “ngForm”。#v = thing语法的意思是——在当前视图中创建一个局部变量。这里我们为视图中的ngFrom创建了一个别名,并绑定到变量#f。这个ngForm来自哪里呢?它来自ngFrom指令导出的。
ngForm是什么类型的对象?它是FormGroup类型的对象。这意味着我们可以在视图中把变量f当作FormGroup使用,而这正是输出事件(ngSubmit)中使用方法。
注意:在Angular中NgForm会自动附加到<form>标签上(应为ngForm指令的选择器默认包含了所有的form),这意味着我们可以不必添加ngForm属性就能使用NgForm指令,但是在上述例子中我们依然将ngForm添加到属性的值上。这是为何?
这里需要说明的是:如果ngForm是属性的值,那就是在告述Angular:我们要根据这个属性使用NgForm指令。但在这里,我们需要对一个引用(#f)赋值,因此把ngForm用作属性值。这表示把ngForm这个表达式的执行结果赋值给局部变量f上。既然ngForm在这个节点上,我们可以知道通过ngForm指令导出的这个f变量是FormGroup类型的,接下来就可以在视图的任何地方引用它了。
我们在表单中绑定ngSubmit事件的语法是:(ngSubmit) = "onSubmit(f.value)"。
1⃣️(ngSubmit):来自NgForm指令
2⃣️onSubmit():将会在我们的组件类中进行定义(稍后定义)
3⃣️f.value:f就是前面提到的FormGroup,而.value会以键值对的形式返回FormGroup中的所有控件的值。
总结起来,这行代码的意思是:“当我们提交表单时,将会以该表单的值作为参数,调用组件实例上的onSubmit方法”。
(2)input和NgModel
示例代码:
<input type="text" name="sku" ngModel id="skuinput" placeholder="SKU">
NgModel指令指的是selector是ngModel。这意味着我们可以通过添加这个属性把它附加到input标签上:ngmodel = “whatever”。这里我们指定了一个不带属性值的ngModel。
有两种不同的方法能在模版中指定ngModel,这里使用的是第一种。当使用不带属性值的ngModel时,我们需要指定:
1⃣️单向数据绑定
2⃣️希望在表单中创建一个名为sku的FormControl(这个sku来自input标签的name属性)
NgModel会创建一个新的FormCOntrol对象,把它自身添加到父FormGroup上(这里也就是form表单对象上),并把这个FormControl对象绑定到一个DOM上,也就是说,它会在视图input标签和FormControl对象之间建立关联。这种关联通过name属性建立的,在本例中是“sku”。
注意:NgModel和ngModel有什么不同呢?通常,我们使用Pascal命名法(如NgModel)时,指的是类和供代码中引用的对象。首字母小写的驼峰命名法(如ngModel)来自指令的选择器selector,并且只会被用在DOM/模版中。需要指出的是,NgModel和FormControl并不是同一个。NgModel是用在视图中的指令,而FormControl则用来表示表单中的数据和校验规则。有时我们希望使用ngModel来实现AngularJS那样的双向数据绑定。
现在来看组件类的实现,代码如下:
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-demo-form-sku',
templateUrl: './demo-form-sku.component.html',
styleUrls: ['./demo-form-sku.component.css']
})
export class DemoFormSkuComponent implements OnInit {
constructor() { }
ngOnInit() {
}
onSubmit(form: any): void {
console.log("you submited value:", form);
}
}
(3)使用FormBuilder(响应式表单)
使用ngForm和ngModel隐式构建FormGroup和FormControl确实很方便,但无法为我们提供更多的定制化选项。使用FormBuilder构建表单则是一种更为灵活和通用的方式。
FormBuilder是一个名副其实的表单构建助手。我们知道表单由FormControl和FormGroup构成,而FormBuilder则可以帮组我们创建它们(你可以把它看作一个“工厂”)。
使用FormBuilder需要导入FormGroup和FormBuilder类。导入代码如下(部分代码未显示):
import { Component, OnInit } from '@angular/core';
import { FormGroup, FormBuilder } from '@angular/forms';
@Component({
selector: 'app-demo-form-builder',
templateUrl: './demo-form-builder.component.html',
styleUrls: ['./demo-form-builder.component.css']
})
通过在组建上声明带参数的contructor,我们注入了一个FormBuilder。代码如下:
export class DemoFormBuilderComponent implements OnInit {
myForm: FormGroup;
constructor(fb: FormBuilder) {
this.myForm = fb.group({
'sku': ['ABC123']
});
}
onSubmit(form: any): void {
console.log("you submited value :", form);
}
ngOnInit() {
}
}
我们通过Angular注入一个从FormBuilder类创建的对象实例,并把它赋值给fb变量。FormBuilder中有两个主要函数:
1⃣️control,用于创建一个新的FormControl对象;
2⃣️group,用于创建一个新的FormGroup对象
在上述代码中,myForm是一个FormGroup类型。我们通过调用fb.group()来创建FormGroup。group()方法的参数是代表组内各个FormControl的键值对。
在这里,我们设置了一个名为sku的控件,其值为[“ABC123“]——意思是其默认值为“ABC123”。(你可能注意到这是一个数组。这是因为稍后还会添加更多配置项)。
现在我们能在视图中使用myfForm了。(也就是说,我们需要将它绑定到表单元素上)。
如何在视图中使用myForm呢?我们说过,当我们导入FormModel时,ngForm就会自动添加到<form>标签上。还提到ngForm还会自动创建自己的FormGroup。但我们不希望使用外部的FormGroup,而是使用FormBuilder创建的myForm实例变量。该如何做?
Angualr为我们提供了另外一个指令,能够让我们使用FormBuilder构建的现有FormGroup。指令名称为:formGroup,可以这样使用。代码如下:
<div>
<h2>Demo Form: Sku</h2>
<form [formGroup] = "myForm" (ngSubmit) = "onSubmit(myForm.value)" class="ui form">
<div class="field">
<label for="skuinput">SKU</label>
<input type="text" [formControl] = "myForm.controls['sku']" id="skuinput" placeholder="SKU">
</div>
<button type="submit" class="ui button">Submit</button>
</form>
</div>
正如上述代码所示,我们需要把onSubmit中的f替换成myForm,应为现在的myForm变量中保存着表单的配置和值。另外,想要让程序正常执行,还需要做最后一件事:将我们的FormControl绑定到<input>标签上。记住ngControl会创建一个FormControl对象,并附加到父FormGroup中。但在该例子中我们已经使用FormBuilder创建了自己的FormControl。在这里我们将<input>标签上的formControl指令指向myForm.controls上现有的FormControl控件sku即可。
创建表单你需要记住以下两点:
1⃣️如果想要隐式创建新的FormGroup和FormControl,使用:
ngForm
ngModel
2⃣️如果要绑定一个现有的FormGroup和FormControl,使用:
formGroup
formControl
(4)添加验证