在本教程中,我们将讨论可用于在 angular 中开发表单的不同策略。无论您选择何种策略,以下是表单库应涵盖的内容:
支持双向绑定,使输入控件值与组件状态同步。
跟踪表单状态并使用视觉提示让用户知道当前状态是否有效。例如,如果用户名包含无效字符,则用户名的输入字段周围应出现红色边框。
有一种机制可以正确显示验证错误。
除非满足某些验证标准,否则启用或禁用表单的某些部分。
Angular 中的表单简介
Angular 作为一个成熟的前端框架,有自己的一套库来构建复杂的表单。最新版本的 Angular 有两种强大的表单构建策略。他们是:
模板驱动的表单
模型驱动或反应形式
这两种技术都属于@angular/forms库,并且基于相同的表单控件类。然而,它们在哲学、编程风格和技术上有显着差异。选择一个而不是另一个取决于您的个人品味以及您要创建的表单的复杂性。在我看来,您应该先尝试这两种方法,然后选择适合您的风格和手头项目的方法。
本教程的第一部分将通过一个实际示例介绍模板驱动的表单:构建一个对所有表单字段进行验证的注册表单。在本教程的第二部分,我们将回顾使用模型驱动方法创建相同表单的步骤。
模板驱动的表单
模板驱动方法是从 AngularJS 时代借用的策略。在我看来,这是构建表单最直接的方法。它是如何工作的?我们将使用一些 Angular 指令。
指令允许您将行为附加到 dom 中的元素。— Angular 文档
Angular 提供了特定于表单的指令,您可以使用它们来绑定表单输入数据和模型。特定于表单的指令向纯 html 表单添加额外的功能和行为。最终结果是模板负责绑定值与模型和表单验证。
在本教程中,我们将使用模板驱动的表单来创建应用程序的注册页面。表单将涵盖最常见的表单元素和对这些表单元素的不同验证检查。以下是您将在本教程中遵循的步骤。
添加FormsModule到app.module.ts.
为模型创建一个类User。
为注册表单创建初始组件和布局。
使用 Angular 表单指令,如ngModel、ngModelGroup和ngForm。
使用内置验证器添加验证。
有意义地显示验证错误。
使用 处理表单提交ngSubmit。
让我们开始吧。
先决条件
该项目的代码可在我的GitHub存储库中找到。下载 zip 或克隆 repo 以查看它的实际效果。如果您更喜欢从头开始,请确保您安装了 Angular CLI。使用ng命令生成一个新项目。
$ ng new SignupFormProject
接下来,为 SignupForm 生成一个新组件。
ng generate component SignupForm
将app.component.html的内容替换为:
<app-signup-form> </app-signup-form>
这是src/目录的目录结构。为了简单起见,我删除了一些非必要的文件。
1 . 2 ├── app 3 │ ├── app.component.css 4 │ ├── app.component.html 5 │ ├── app.component.ts 6 │ ├── app.module.ts 7 │ ├── signup-form 8 │ │ ├── signup-form.component.css 9 │ │ ├── signup-form.component.html 10 │ │ └── signup-form.component.ts 11 │ └── User.ts 12 ├── index.html 13 ├── main.ts 14 ├── polyfills.ts 15 ├── styles.css 16 ├── tsconfig.app.json 17 └── typings.d.ts
如您所见,组件的目录SignupForm已自动创建。这就是我们大部分代码的去向。我还创建了一个新的User.ts用于存储我们的用户模型。
HTML 模板
在深入研究实际的组件模板之前,我们需要对正在构建的内容有一个抽象的概念。所以这是我脑海中的表单结构。注册表单将有几个输入字段、一个选择元素和一个复选框元素。
这是我们将用于注册页面的 HTML 模板。
网页模板
1 <div class="row custom-row"> 2 <div class= "col-sm-5 custom-container jumbotron"> 3 4 <form class="form-horizontal"> 5 <fieldset> 6 <legend>SignUp</legend> 7 8 <!--- Email Block ---> 9 <div class="form-group"> 10 <label for="inputEmail">Email</label> 11 <input type="text" 12 id="inputEmail" 13 placeholder="Email"> 14 </div> 15 <!--- Password Block ---> 16 <div class="form-group"> 17 <label for="inputPassword">Password</label> 18 <input type="password" 19 id="inputPassword" 20 placeholder="Password"> 21 </div> 22 23 <div class="form-group"> 24 <label for="confirmPassword" >Confirm Password</label> 25 <input type="password" 26 id="confirmPassword" 27 placeholder="Password"> 28 </div> 29 30 <!--- Select gender Block ---> 31 <div class="form-group"> 32 <label for="select">Gender</label> 33 <select id="select"> 34 <option>Male</option> 35 <option>Female</option> 36 <option>Other</option> 37 </select> 38 </div> 39 40 <!--- Terms and conditions Block ---> 41 <div class="form-group checkbox"> 42 <label> 43 <input type="checkbox"> Confirm that you've read the Terms and 44 Conditions 45 </label> 46 </div> 47 48 <!--- Buttons Block ---> 49 <div class="form-group"> 50 <button type="reset" class="btn btn-default">Cancel</button> 51 <button type="submit" class="btn btn-primary">Submit</button> 52 </div> 53 </fieldset> 54 </form> 55 </div> 56 </div>
HTML 模板中使用的 CSS 类是用于使事情变得漂亮的 Bootstrap 库的一部分。由于这不是设计教程,除非必要,否则我不会过多谈论表单的 CSS 方面。
基本表格设置
要使用模板驱动的表单指令,我们需要导入FormsModulefrom@angular/forms并将其添加到app.module.tsimport中的数组中。
应用程序/app.module.ts
1 import { FormsModule } from '@angular/forms'; 2 @NgModule({ 3 . 4 . 5 imports: [ 6 BrowserModule, 7 FormsModule 8 ], 9 . 10 . 11 }) 12 export class AppModule { }
接下来,创建一个类来保存User实体的所有属性。我们可以使用接口并在组件中实现它,也可以为模型使用 typescript 类。
应用程序/User.ts
1 export class User { 2 constructor( 3 public id: number, 4 public email: string, 5 public pwd: string, 6 public confirmPwd: string, 7 public gender: string, 8 public terms: boolean 9 ) { } 10 11 }
现在,在组件中创建类的实例SignupForm。我
应用程序/注册表单/注册表单.component.ts
1 import { Component } from '@angular/core'; 2 import { User } from '../user'; 3 4 @Component({ 5 selector: 'app-signup-form', 6 templateUrl: './signup-form.component.html', 7 styleUrls: ['./signup-form.component.scss'] 8 }) 9 export class SignupFormComponent { 10 gender = ['Male','Female','Other'] 11 12 user = new User(1,'john@example.com','secret', 'secret', this.gender[1], true); 13 14 }
对于signup-form.component.html文件,我将使用上面讨论的相同 HTML 模板,但稍作更改。注册表单有一个带有选项列表的选择字段。虽然这行得通,但我们将通过使用指令循环遍历列表来使用 Angular 方式ngFor。
应用程序/注册表格/注册表格.component.html
1 <div class="row custom-row"> 2 <div class= "col-sm-5 custom-container jumbotron"> 3 4 <form class="form-horizontal"> 5 <fieldset> 6 <legend>SignUp</legend> 7 . 8 . 9 <!--- Gender Block --> 10 <div class="form-group"> 11 <label for="select">Gender</label> 12 <select id="select"> 13 14 <option *ngFor = "let g of gender" 15 [value] = "g"> {{g}} 16 </option> 17 </select> 18 </div> 19 . 20 . 21 </fieldset> 22 </form> 23 </div> 24 </div>
接下来,我们要将表单数据绑定到用户类对象,以便在您将注册数据输入表单时,创建一个新的用户对象来临时存储该数据。这样,您可以使视图与模型保持同步,这称为绑定。
有几种方法可以实现这一点。首先让我向您介绍ngModel和ngForm。
ngForm和ngModel
ngForm并且ngModel是创建模板驱动表单所必需的 Angular 指令。让我们首先开始ngForm。ngForm这是有关Angular 文档的摘录。
ngForm创建一个顶级 FormGroup 实例并将其绑定到一个 <form> 元素以跟踪聚合的表单值和验证状态。一旦你 import FormsModule,这个指令就默认在所有 <form> 标签上激活。您不需要添加特殊的选择器。
首先,使用指令更新表单ngForm:
应用程序/注册表格/注册表格.component.html
1 <form 2 class="form-horizontal" 3 #signupForm = "ngForm"> 4 . 5 . 6 </form>
#signupForm是一个模板引用变量,它引用ngForm管理整个表单的指令。下面的示例演示了使用ngForm引用对象进行验证。
应用程序/注册表格/注册表格.component.html
1 <button 2 type="submit" 3 class="btn btn-success" 4 [disabled]="!signupForm.form.valid"> 5 Submit 6 </button>
在这里,signupForm.form.valid除非所有表单元素都通过各自的验证检查,否则将返回 false。在表单有效之前,提交按钮将被禁用。
至于绑定模板和模型,有很多方法可以做到这一点,并且ngModel有三种不同的语法来解决这种情况。他们是:
[(ngModel)]
[ngModel]
ngModel
让我们从第一个开始。
双向绑定使用[(ngModel)]
[(ngModel)]执行双向绑定以读取和写入输入控制值。如果[(ngModel)]使用指令,则输入字段从绑定的组件类中获取初始值,并在检测到输入控件值发生任何更改时(在击键和按下按钮时)将其更新回来。下图更好地描述了双向绑定过程。
这是电子邮件输入字段的代码:
1 <div class="form-group"> 2 <label for="inputEmail">Email</label> 3 <input type="text" 4 [(ngModel)] = "user.email" 5 id="inputEmail" 6 name="email" 7 placeholder="Email"> 8 </div>
[(ngModel)] = "user.email"将用户的电子邮件属性绑定到输入值。我还添加了一个名称属性并设置了name="email". 这很重要,如果您在使用 ngModel 时没有声明 name 属性,您将得到一个错误。
同样,为每个表单元素添加一个[(ngModel)]唯一的名称属性。您的表单现在应该看起来像这样:
应用程序/注册表格/注册表格.component.html
1 . 2 . 3 . 4 <div ngModelGroup="password"> 5 <div class="form-group" > 6 <label for="inputPassword">Password</label> 7 <input type="password" 8 [(ngModel)] = "user.pwd" name="pwd" 9 placeholder="Password"> 10 </div> 11 <div class="form-group"> 12 <label for="confirmPassword" >Confirm Password</label> 13 <input type="password" 14 [(ngModel)] = "user.confirmPwd" name="confirmPwd" 15 placeholder="Confirm Password"> 16 </div> 17 </div> 18 <div class="form-group"> 19 <label for="select">Gender</label> 20 <select id="select" 21 [(ngModel)] = "user.gender" name = "gender"> 22 23 <option *ngFor = "let g of gender" 24 [value] = "g"> {{g}} 25 </option> 26 </select> 27 </div> 28 29 . 30 . 31 .
用于ngModelGroup将相似的表单字段组合在一起,以便我们可以仅对这些表单字段运行验证。由于两个密码字段都是相关的,我们将把它们放在一个 ngModelGroup 下。如果一切都按预期工作,则组件绑定user属性应该负责存储所有表单控件值。要查看实际效果,请在表单标记后添加以下内容:
{{user | json}}
通过管道传递用户属性,以JsonPipe在浏览器中将模型呈现为 JSON。这有助于调试和记录。您应该会看到这样的 JSON 输出。
值从视图流入模型。反过来呢?尝试用一些值初始化用户对象。
应用程序/注册表单/注册表单.component.ts
1 newUser() { 2 this.user = new User(2, 'thisisfromthemodel@example.com', '','','Male',false); 3 } 它们会自动出现在视图中: 1 { "email": "thisisfromthemodel@example.com", 2 "pwd": "", "confirm_pwd": "", 3 "gender": "Male" 4 }
双向绑定[(ngModel)]语法可帮助您毫不费力地构建表单。但是,它有一些缺点;因此,有一种替代方法使用ngModelor [ngModel]。
添加ngModel到组合
当ngModel使用时,我们实际上负责使用输入控件值更新组件属性,反之亦然。输入数据不会自动流入组件的用户属性。
因此,将所有实例替换[(ngModel)] = " "为ngModel。我们将保留该name属性,因为所有三个版本的 ngModel 都需要该name属性才能工作。
应用程序/注册表格/注册表格.component.html
1 <div class="form-group"> 2 <label for="inputEmail">Email</label> 3 <input type="text" 4 ngModel 5 id="inputEmail" 6 name="email" 7 placeholder="Email"> 8 </div>
使用,name 属性的值将成为我们之前创建的引用对象ngModel的键。因此,例如,将存储电子邮件 ID 的控制值。ngFormsignupFormsignupForm.value.email
替换{{user | json}}为,{{signupForm.value | json }}因为那是现在存储所有状态的地方。
单向绑定使用[ngModel]
如果您需要从绑定类组件设置初始状态怎么办?这就是[ngModel]为你做的。
这里数据从模型流向视图。对语法进行以下更改以使用单向绑定:
应用程序/注册表格/注册表格.component.html
1 <div class="form-group"> 2 <label for="inputEmail">Email</label> 3 <input type="text" 4 [ngModel] = "user.email" 5 id="inputEmail" 6 name="email" 7 placeholder="Email"> 8 </div>
那么您应该选择哪种方法呢?如果你同时使用[(ngModel)]and ngForm,你最终将有两种状态要维护——user和signupForm.value——这可能会造成混淆。
1 { "email": "thisisfromthemodel@example.com", 2 "pwd": "thisispassword", "confirm_pwd": "thisispassword", 3 "gender": "Male" 4 } //user.value 5 { "email": "thisisfromthemodel@example.com", 6 "pwd": "thisispassword", "confirm_pwd": "thisispassword", 7 "gender": "Male" 8 } //signupForm.value
因此,我建议改用单向绑定方法。但那是你要决定的事情。
验证和显示错误消息
这是我们的验证要求。
所有表单控件都是必需的。
禁用提交按钮,直到填写所有输入字段。
电子邮件字段应严格包含电子邮件 ID。
密码字段的最小长度应为 8。
密码和确认都应该匹配。
第一个很简单。您required必须像这样向每个表单元素添加一个验证属性:
应用程序/注册表格/注册表格.component.html
1 <input type="text" 2 [ngModel] = "user.email" name="email" 3 #email = "ngModel" 4 placeholder="Email" 5 required>
除了required属性之外,我还导出了一个新的#email模板引用变量。这样您就可以从模板本身访问输入框的 Angular 控件。我们将使用它来显示错误和警告。现在使用按钮的 disabled 属性来禁用按钮:
应用程序/注册表格/注册表格.component.html
1 <button 2 type="submit" 3 class="btn btn-success" 4 [disabled]="!signupForm.form.valid"> 5 Submit 6 </button>
要在电子邮件上添加约束,请使用与输入字段一起使用的模式属性。模式用于指定正则表达式,如下所示:
pattern="[a-z0-9._%+-]+@[a-z0-9.-]+\.[az]{2,3}$"
对于密码字段,您所要做的就是添加一个minlength=" "属性:
应用程序/注册表格/注册表格.component.html
1 <input type="password" 2 ngModel 3 id="inputPassword" 4 name="pwd" 5 #pwd = "ngModel" 6 placeholder="Password" 7 minlength="8" 8 required>
为了显示错误,我将ngIf在 div 元素上使用条件指令。让我们从电子邮件的输入控制字段开始:
应用程序/注册表格/注册表格.component.html
1 <div class="form-group"> 2 <label for="inputEmail">Email</label> 3 <input type="text" 4 [ngModel] = "user.email" name="email" 5 #email = "ngModel" id="inputEmail" 6 placeholder="Email" 7 pattern="[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,3}$" 8 required> 9 </div> 10 <!-- This is the error section --> 11 <div *ngIf="email.invalid && (email.dirty || email.touched)" 12 class="alert alert-danger"> 13 <div *ngIf = "email.errors?.required"> 14 Email field can't be blank 15 </div> 16 <div *ngIf = "email.errors?.pattern && email.touched"> 17 The email id doesn't seem right 18 </div> 19 </div>
这里发生了很多事情。让我们从错误部分的第一行开始。
1 <div *ngIf="email.invalid && (email.dirty || email.touched)" 2 class="alert alert-danger"
还记得#email我们之前导出的变量吗?它包含一些关于电子邮件字段的输入控件状态的信息。这包括:email.valid、email.invalid、email.dirty、email.pristine、email.touched、email.untouched和email.errors。下图详细描述了每个属性。
*ngIf因此,仅当电子邮件无效时才会呈现带有 的div 元素。但是,即使在用户有机会编辑表单之前,用户也会收到有关输入字段为空的错误。
为避免这种情况,我们添加了第二个条件。只有在访问控件或更改控件的值后才会显示该错误。
嵌套的 div 元素用于覆盖所有验证错误的情况。我们用来email.errors检查所有可能的验证错误,然后以自定义消息的形式将它们显示给用户。现在,对其他表单元素执行相同的过程。以下是我对密码验证进行编码的方式。
应用程序/注册表格/注册表格.component.html
1 <div ngModelGroup="password" #userPassword="ngModelGroup" required > 2 <div class="form-group"> 3 <label for="inputPassword">Password</label> 4 <input type="password" 5 ngModel name="pwd" 6 id="inputPassword" placeholder="Password" 7 minlength ="8" required> 8 </div> 9 <div class="form-group"> 10 <label for="confirmPassword" >Confirm Password</label> 11 <input type="password" 12 ngModel name="confirmPwd" 13 id="confirmPassword" placeholder="Confirm Password"> 14 </div> 15 16 17 <div *ngIf="(userPassword.invalid|| userPassword.value?.pwd != userPassword.value?.confirmPwd) && (userPassword.touched)" 18 class="alert alert-danger"> 19 20 <div *ngIf = "userPassword.invalid; else nomatch"> 21 Password needs to be more than 8 characters 22 </div> 23 <ng-template #nomatch > 24 Passwords don't match 25 </ng-template> 26 </div> 27 </div>
这开始看起来有点混乱。Angular 有一组有限的验证器属性:required、minlength、maxlength和pattern。ngIf要涵盖密码比较等任何其他场景,您将不得不像我上面那样依赖嵌套条件。或者理想情况下,创建一个自定义验证器函数,我将在本系列的第三部分中介绍。
在上面的代码中,我使用了ngIf else最新版本的 Angular 中引入的语法。下面是它的工作原理:
1 <div *ngIf="isValid;else notvalid"> 2 Valid content... 3 </div> 4 <ng-template #notValid>Not valid content...</ng-template>
使用 ngSubmit 提交表单
我们几乎完成了表格。现在我们需要能够提交表单,表单数据的控制应该交给一个组件方法,比如说 onSubmit.
应用程序/注册表格/注册表格.component.html
1 <form class="form-horizontal" novalidate 2 onFormSubmit(signupForm) 3 (ngSubmit)= "onSubmit(signupForm)" 4 #signupForm="ngForm">
现在,对于组件:
应用程序/注册表单/注册表单.component.ts
1 onSubmit({value, valid}: NgForm) { 2 console.log( this.user.email); 3 console.log("valid: " + valid); 4 }
概括
我们都在这里完成了。在本教程中,我们涵盖了使用模板驱动方法在 Angular 中创建表单所需了解的所有内容。模板驱动的表单因其简单易用而广受欢迎。
但是,如果您需要构建一个包含大量表单元素的表单,这种方法就会变得凌乱。因此,在下一个教程中,我们将介绍构建相同表单的模型驱动方式。
- HTML 模板
- 网页模板
- 应用程序/app.module.ts
- 应用程序/User.ts
- 应用程序/注册表单/注册表单.component.ts
- 应用程序/注册表格/注册表格.component.html
- 应用程序/注册表格/注册表格.component.html
- 应用程序/注册表格/注册表格.component.html
- 应用程序/注册表格/注册表格.component.html
- 应用程序/注册表单/注册表单.component.ts
- 应用程序/注册表格/注册表格.component.html
- 应用程序/注册表格/注册表格.component.html
- 应用程序/注册表格/注册表格.component.html
- 应用程序/注册表格/注册表格.component.html
- 应用程序/注册表格/注册表格.component.html
- 应用程序/注册表格/注册表格.component.html
- 应用程序/注册表格/注册表格.component.html
- 应用程序/注册表格/注册表格.component.html
- 应用程序/注册表单/注册表单.component.ts
发表评论