HoRain云小助手个人主页 个人专栏: 《Linux 系列教程》《c语言教程》⛺️生活的理想就是为了理想的生活!⛳️ 推荐前些天发现了一个超棒的服务器购买网站性价比超高大内存超划算忍不住分享一下给大家。点击跳转到网站。专栏介绍专栏名称专栏介绍《C语言》本专栏主要撰写C干货内容和编程技巧让大家从底层了解C把更多的知识由抽象到简单通俗易懂。《网络协议》本专栏主要是注重从底层来给大家一步步剖析网络协议的奥秘一起解密网络协议在运行中协议的基本运行机制《docker容器精解篇》全面深入解析 docker 容器从基础到进阶涵盖原理、操作、实践案例助您精通 docker。《linux系列》本专栏主要撰写Linux干货内容从基础到进阶知识由抽象到简单通俗易懂帮你从新手小白到扫地僧。《python 系列》本专栏着重撰写Python相关的干货内容与编程技巧助力大家从底层去认识Python将更多复杂的知识由抽象转化为简单易懂的内容。《试题库》本专栏主要是发布一些考试和练习题库涵盖软考、HCIE、HRCE、CCNA等目录⛳️ 推荐专栏介绍Angular 2 表单详解1. 模板驱动表单基本配置基本用法2. 响应式表单基本配置基本用法3. 自定义验证器同步验证器异步验证器在表单中使用4. 动态表单5. 表单最佳实践创建可复用表单组件使用6. 表单样式总结对比Angular 2 表单详解Angular 2 提供了两种表单处理方式模板驱动表单​ 和响应式表单。以下是详细的介绍和用法。1. 模板驱动表单适用于简单表单场景大部分逻辑在模板中定义。基本配置// app.module.ts import { NgModule } from angular/core; import { BrowserModule } from angular/platform-browser; import { FormsModule } from angular/forms; // 必须导入 import { AppComponent } from ./app.component; NgModule({ declarations: [AppComponent], imports: [BrowserModule, FormsModule], // 添加 FormsModule bootstrap: [AppComponent] }) export class AppModule { }基本用法!-- app.component.html -- form #myFormngForm (ngSubmit)onSubmit(myForm) !-- 文本输入 -- div label forname姓名/label input typetext idname namename [(ngModel)]user.name #namengModel required minlength2 div *ngIfname.invalid (name.dirty || name.touched) div *ngIfname.errors?.[required]姓名必填/div div *ngIfname.errors?.[minlength]至少2个字符/div /div /div !-- 下拉框 -- div label forgender性别/label select idgender namegender [(ngModel)]user.gender required option value请选择/option option valuemale男/option option valuefemale女/option /select /div !-- 复选框 -- div label input typecheckbox nameagree [(ngModel)]user.agree required 我同意条款 /label /div !-- 单选按钮 -- div label input typeradio namerole valueuser [(ngModel)]user.role 普通用户 /label label input typeradio namerole valueadmin [(ngModel)]user.role 管理员 /label /div button typesubmit [disabled]myForm.invalid提交/button /form// app.component.ts import { Component } from angular/core; Component({ selector: app-root, templateUrl: ./app.component.html }) export class AppComponent { user { name: , gender: , agree: false, role: user }; onSubmit(form: any) { if (form.valid) { console.log(表单数据, this.user); // 发送到服务器... } } }2. 响应式表单适用于复杂表单逻辑在组件类中定义更灵活、可测试。基本配置// app.module.ts import { NgModule } from angular/core; import { BrowserModule } from angular/platform-browser; import { ReactiveFormsModule } from angular/forms; // 必须导入 import { AppComponent } from ./app.component; NgModule({ declarations: [AppComponent], imports: [BrowserModule, ReactiveFormsModule], // 添加 ReactiveFormsModule bootstrap: [AppComponent] }) export class AppModule { }基本用法// app.component.ts import { Component, OnInit } from angular/core; import { FormGroup, FormControl, FormBuilder, Validators, FormArray } from angular/forms; Component({ selector: app-root, templateUrl: ./app.component.html }) export class AppComponent implements OnInit { // 方式1使用 FormBuilder推荐 userForm: FormGroup; // 方式2直接创建 FormGroup // userForm new FormGroup({ // name: new FormControl(, Validators.required), // email: new FormControl(, [Validators.required, Validators.email]) // }); constructor(private fb: FormBuilder) {} ngOnInit() { this.initForm(); } initForm() { this.userForm this.fb.group({ // 单个控件 name: [, [Validators.required, Validators.minLength(2)]], email: [, [Validators.required, Validators.email]], gender: [male], agree: [false, Validators.requiredTrue], // 嵌套表单组 address: this.fb.group({ city: [], street: [] }), // 表单数组 hobbies: this.fb.array([ this.fb.control() ]) }); // 值变化监听 this.userForm.valueChanges.subscribe(value { console.log(表单值变化, value); }); // 状态变化监听 this.userForm.statusChanges.subscribe(status { console.log(表单状态, status); }); } // 获取表单控件 get name() { return this.userForm.get(name); } get email() { return this.userForm.get(email); } get address() { return this.userForm.get(address) as FormGroup; } get hobbies() { return this.userForm.get(hobbies) as FormArray; } // 添加爱好 addHobby() { this.hobbies.push(this.fb.control()); } // 移除爱好 removeHobby(index: number) { this.hobbies.removeAt(index); } onSubmit() { if (this.userForm.valid) { console.log(表单数据, this.userForm.value); console.log(原始值, this.userForm.getRawValue()); // 重置表单 // this.userForm.reset(); // this.userForm.reset({ gender: male }); // 重置为默认值 } else { // 标记所有控件为 touched 以显示错误 this.markFormGroupTouched(this.userForm); } } // 工具方法标记所有控件为 touched markFormGroupTouched(formGroup: FormGroup) { Object.values(formGroup.controls).forEach(control { control.markAsTouched(); if (control instanceof FormGroup) { this.markFormGroupTouched(control); } }); } // 设置表单值 setFormValues() { this.userForm.patchValue({ name: 张三, email: zhangsanexample.com, address: { city: 北京, street: 朝阳区 } }); // 或者使用 setValue必须提供所有字段 // this.userForm.setValue({...}); } }!-- app.component.html -- form [formGroup]userForm (ngSubmit)onSubmit() !-- 文本输入 -- div label forname姓名/label input typetext idname formControlNamename [class.is-invalid]name.invalid (name.dirty || name.touched) div *ngIfname.invalid (name.dirty || name.touched) classerror div *ngIfname.errors?.[required]姓名必填/div div *ngIfname.errors?.[minlength] 至少{{ name.errors?.[minlength].requiredLength }}个字符 /div /div /div !-- 邮箱输入 -- div label foremail邮箱/label input typeemail idemail formControlNameemail div *ngIfemail.invalid (email.dirty || email.touched) classerror div *ngIfemail.errors?.[required]邮箱必填/div div *ngIfemail.errors?.[email]邮箱格式不正确/div /div /div !-- 嵌套表单组 -- div formGroupNameaddress div label forcity城市/label input typetext idcity formControlNamecity /div div label forstreet街道/label input typetext idstreet formControlNamestreet /div /div !-- 表单数组 -- div formArrayNamehobbies h3爱好/h3 div *ngForlet hobby of hobbies.controls; let i index input [formControlName]i placeholder爱好 button typebutton (click)removeHobby(i) *ngIfhobbies.length 1 删除 /button /div button typebutton (click)addHobby()添加爱好/button /div !-- 复选框 -- div label input typecheckbox formControlNameagree 我同意条款 /label div *ngIfuserForm.get(agree).invalid (userForm.get(agree).dirty || userForm.get(agree).touched) classerror 必须同意条款 /div /div !-- 下拉框 -- div label forgender性别/label select idgender formControlNamegender option valuemale男/option option valuefemale女/option /select /div button typesubmit [disabled]userForm.invalid提交/button button typebutton (click)setFormValues()填充数据/button button typebutton (click)userForm.reset()重置/button /form !-- 调试信息 -- div h3表单状态/h3 p表单值{{ userForm.value | json }}/p p表单状态{{ userForm.status }}/p p姓名控件状态{{ name?.status }}/p p邮箱控件错误{{ email?.errors | json }}/p /div3. 自定义验证器同步验证器// validators/password.validator.ts import { AbstractControl, ValidationErrors, ValidatorFn } from angular/forms; // 密码强度验证器 export function passwordValidator(): ValidatorFn { return (control: AbstractControl): ValidationErrors | null { const value control.value; if (!value) return null; const hasNumber /\d/.test(value); const hasUpper /[A-Z]/.test(value); const hasLower /[a-z]/.test(value); const hasSpecial /[!#$%^*]/.test(value); const valid hasNumber hasUpper hasLower hasSpecial; return !valid ? { passwordStrength: { hasNumber, hasUpper, hasLower, hasSpecial, message: 密码必须包含数字、大小写字母和特殊字符 } } : null; }; } // 确认密码验证器 export function confirmPasswordValidator(controlName: string, matchingControlName: string) { return (formGroup: AbstractControl): ValidationErrors | null { const control formGroup.get(controlName); const matchingControl formGroup.get(matchingControlName); if (!control || !matchingControl) return null; if (matchingControl.errors !matchingControl.errors[passwordMismatch]) { return null; } if (control.value ! matchingControl.value) { matchingControl.setErrors({ passwordMismatch: true }); return { passwordMismatch: true }; } else { matchingControl.setErrors(null); return null; } }; }异步验证器// validators/username.validator.ts import { AbstractControl, AsyncValidatorFn, ValidationErrors } from angular/forms; import { Observable, of } from rxjs; import { map, catchError, delay } from rxjs/operators; export function usernameValidator(): AsyncValidatorFn { return (control: AbstractControl): ObservableValidationErrors | null { // 模拟API调用 return of(control.value).pipe( delay(1000), // 模拟网络延迟 map(value { // 模拟已存在的用户名 const takenUsernames [admin, user, test]; return takenUsernames.includes(value) ? { usernameTaken: true } : null; }), catchError(() of(null)) ); }; }在表单中使用// 在组件中 import { passwordValidator, confirmPasswordValidator, usernameValidator } from ./validators; this.registerForm this.fb.group({ username: [, [Validators.required, Validators.minLength(3)], [usernameValidator()] // 异步验证器 ], password: [, [Validators.required, passwordValidator()]], confirmPassword: [, Validators.required] }, { validator: confirmPasswordValidator(password, confirmPassword) });4. 动态表单根据数据动态生成表单。// dynamic-form.component.ts import { Component, OnInit } from angular/core; import { FormGroup, FormControl, Validators } from angular/forms; interface FieldConfig { type: text | email | number | select | checkbox; name: string; label: string; value?: any; placeholder?: string; validators?: any[]; options?: { label: string, value: any }[]; } Component({ selector: app-dynamic-form, template: form [formGroup]dynamicForm (ngSubmit)onSubmit() div *ngForlet field of fieldConfigs !-- 文本、邮箱、数字 -- div *ngIf[text, email, number].includes(field.type) label [for]field.name{{ field.label }}/label input [type]field.type [id]field.name [formControlName]field.name [placeholder]field.placeholder /div !-- 下拉框 -- div *ngIffield.type select label [for]field.name{{ field.label }}/label select [id]field.name [formControlName]field.name option *ngForlet option of field.options [value]option.value {{ option.label }} /option /select /div !-- 复选框 -- div *ngIffield.type checkbox label input typecheckbox [formControlName]field.name {{ field.label }} /label /div !-- 显示验证错误 -- div *ngIfdynamicForm.get(field.name)?.invalid dynamicForm.get(field.name)?.touched classerror div *ngIfdynamicForm.get(field.name)?.hasError(required) {{ field.label }} 必填 /div /div /div button typesubmit [disabled]dynamicForm.invalid提交/button /form }) export class DynamicFormComponent implements OnInit { dynamicForm: FormGroup new FormGroup({}); fieldConfigs: FieldConfig[] [ { type: text, name: username, label: 用户名, placeholder: 请输入用户名, validators: [Validators.required] }, { type: email, name: email, label: 邮箱, validators: [Validators.required, Validators.email] }, { type: select, name: country, label: 国家, options: [ { label: 中国, value: CN }, { label: 美国, value: US } ] }, { type: checkbox, name: subscribe, label: 订阅邮件 } ]; ngOnInit() { const group: any {}; this.fieldConfigs.forEach(field { group[field.name] new FormControl( field.value || , field.validators || [] ); }); this.dynamicForm new FormGroup(group); } onSubmit() { console.log(动态表单数据, this.dynamicForm.value); } }5. 表单最佳实践创建可复用表单组件// form-field.component.ts import { Component, Input } from angular/core; import { FormGroup } from angular/forms; Component({ selector: app-form-field, template: div [formGroup]form label [for]name{{ label }}/label input [type]type [id]name [formControlName]name [placeholder]placeholder div *ngIfform.get(name)?.invalid form.get(name)?.touched classerror div *ngIfform.get(name)?.hasError(required) {{ label }} 必填 /div div *ngIfform.get(name)?.hasError(email) 邮箱格式不正确 /div !-- 添加更多错误类型 -- /div /div }) export class FormFieldComponent { Input() form!: FormGroup; Input() name!: string; Input() label!: string; Input() type: string text; Input() placeholder: string ; }使用app-form-field [form]userForm nameusername label用户名 placeholder请输入用户名 /app-form-field6. 表单样式/* forms.css */ input.ng-invalid.ng-touched, select.ng-invalid.ng-touched { border-color: #dc3545; } .error { color: #dc3545; font-size: 0.875em; margin-top: 0.25rem; } .form-group { margin-bottom: 1rem; }总结对比特性模板驱动表单响应式表单复杂度简单复杂可测试性较低高动态控制有限强大验证逻辑模板中定义组件中定义适合场景简单表单复杂表单、动态表单推荐对于新项目建议使用响应式表单因为它更强大、灵活且易于测试。❤️❤️❤️本人水平有限如有纰漏欢迎各位大佬评论批评指正如果觉得这篇文对你有帮助的话也请给个点赞、收藏下吧非常感谢! Stay Hungry Stay Foolish 道阻且长,行则将至,让我们一起加油吧