在angular中,对表单进行操作的方式有两种,第一种为前面我们用过的较简单的[模板驱动表单](https://www.angular.cn/guide/forms),第二种为本小节要使用更加面向对象的[响应式表单](https://www.angular.cn/guide/reactive-forms)。 ## 引入依赖 响应式表单位于[ReactiveFormsModule](https://www.angular.cn/api/forms/ReactiveFormsModule)中。 klass/add/add.component.spec.ts ``` import {FormsModule, ReactiveFormsModule} from '@angular/forms'; ① describe('Klass/AddComponent', () => { let component: AddComponent; let fixture: ComponentFixture<AddComponent>; beforeEach(async(() => { TestBed.configureTestingModule({ declarations: [AddComponent], imports: [ FormsModule, ② ReactiveFormsModule ➊ ] }) .compileComponents(); })); ``` * ② ngSubmit位于FormsModule中 * ➊ 我们即将使用的响应式表单位于ReactiveFormsModule中 ## C层初始化 在响应式表中,每个表单项都是一个对象,使用如下方法进行初始化: klass/add/add.component.ts ① ``` import {FormControl} from '@angular/forms'; export class AddComponent implements OnInit { name: FormControl; ② teacherId: FormControl; ② constructor() { } ngOnInit() { this.name = new FormControl(''); ➊ this.teacherId = new FormControl(); ➊ } onSubmit(): void { } } ``` * ② 变量类型由普通的变量,变更为FormControl对象 * ➊ 实例化 ## 绑定到V层 klass/add/add.component.html ``` <h3>新增班级</h3> <form (ngSubmit)="onSubmit()"> <label for="name">名称:<input id="name" type="text" [formControl]➊="name"/></label> <label for="teacherId">教师ID:<input type="number" id="teacherId" [formControl]➊="teacherId"></label> <button>保存</button> </form> ``` * ➊ 使用`[formControl]`来指定该表单项对应的响应式表单对象。 ## 单元测试 ### 测序C层绑定到V层 klass/add/add.component.spec.ts ``` /** * 测试C层向V层数据绑定 * 在C层中使用setValue方法对表单项赋值 * 重新渲染V层后,使用CSS选择器来获取元素 * 获取元素的值并断言 */ fit('测试C层向V层数据绑定', () => { expect(component).toBeTruthy(); component.name.setValue('test'); component.teacherId.setValue(1); fixture.detectChanges(); fixture.whenStable().then(() => { const debugElement: DebugElement = fixture.debugElement; const nameElement = debugElement.query(By.css('#name')); ① const nameInput: HTMLInputElement = nameElement.nativeElement; expect(nameInput.value).toBe('test'); const teacherIdElement = debugElement.query(By.css('#teacherId')); ① const teacherIdInput: HTMLInputElement = teacherIdElement.nativeElement; expect(teacherIdInput.value).toBe('1'); }); }); ``` * ① 使用`#`来选择`id`元素。 ![](https://img.kancloud.cn/f6/f3/f6f360af71f4fdccbb595767eea12111_445x93.png) ### 测试V层绑定到C层 将前面的测试方法名由`fit`修改为`it`后,新建如下测试方法: klass/add/add.component.spec.ts ``` /** * 测试V层向C层绑定 * 获取V层的元素,并设置元素的值 * 断言在C层中获取到了元素的值 */ fit('测试V层向C层绑定', () => { expect(component).toBeTruthy(); fixture.whenStable().then(() => { const debugElement: DebugElement = fixture.debugElement; const nameElement = debugElement.query(By.css('#name')); const nameInput: HTMLInputElement = nameElement.nativeElement; nameInput.value = 'test2'; nameInput.dispatchEvent(new Event('input')); expect(component.name.value).toBe('test2'); const teacherIdElement = debugElement.query(By.css('#teacherId')); const teacherIdInput: HTMLInputElement = teacherIdElement.nativeElement; teacherIdInput.value = '2'; teacherIdInput.dispatchEvent(new Event('input')); expect(component.teacherId.value).toBe(2); }); }); ``` ![](https://img.kancloud.cn/3a/78/3a78bb1a485388c470b1ff2088060334_458x93.png) # 参考文档 | 名称 | 链接 | 预计学习时长(分) | | --- | --- | --- | | 源码地址 | [https://github.com/mengyunzhi/spring-boot-and-angular-guild/releases/tag/step3.3.2](https://github.com/mengyunzhi/spring-boot-and-angular-guild/releases/tag/step3.3.2) | - | | 响应式表单 | [https://www.angular.cn/guide/reactive-forms](https://www.angular.cn/guide/reactive-forms) | 20 |