Angular 模板驱动的表单

在Angular 2中构建表单最直接的方法是利用为您提供的指令。

首先,考虑一个典型的表单:

<form method="POST" action="/register" id="signup-form">
  <label for="email">Email</label>
  <input type="text" name="email" id="email">
  <label for="password">Password</label>
  <input type="password" name="password" id="password">
  <button type="submit">Sign Up</button>
</form>

Angular 2已经为你提供了一个form指令,并形成在封面下操作的相关指令,如输入等。 对于基本的实现,我们只需要添加一些属性,并确保我们的组件知道如何处理数据。

index.html

<signup-form>Loading...</signup-form>

signup-form.component.html

<form #signupForm="ngForm" (ngSubmit)="registerUser(signupForm)">
  <label for="email">Email</label>
  <input type="text" name="email" id="email" ngModel>
  <label for="password">Password</label>
  <input type="password" name="password" id="password" ngModel>
  <button type="submit">Sign Up</button>
</form>

signup-form.component.ts

import { Component } from '@angular/core';
import { NgForm } from '@angular/forms';
@Component({
  selector: 'app-signup-form',
  templateUrl: 'app/signup-form.component.html',
})
export class SignupFormComponent {
  registerUser(form: NgForm) {
    console.log(form.value);
    // {email: '...', password: '...'}
    // ...
  }
}

嵌套表单数据

如果你发现你在把一维的表单替换成嵌套的数据上遇到了麻烦,不用怕,Angular已经涵盖了简单和复杂的情况。假设你要提交一个类似下面的数据:

{
  "contact": {
    "firstname": "Bob",
    "lastname": "McKenzie",
    "email": "BobAndDoug@GreatWhiteNorth.com",
    "phone": "555-TAKE-OFF"
  },
  "address": {
    "street": "123 Some St",
    "city": "Toronto",
    "region": "ON",
    "country": "CA",
    "code": "H0H 0H0"
  },
  "paymentCard": {
    "provider": "Credit Lending Company Inc",
    "cardholder": "Doug McKenzie",
    "number": "123 456 789 012",
    "verification": "321",
    "expiry": "2020-02"
  }
}

虽然表单是平面和一维的,但从它们构建的数据不是。将您已经提供的数据转换为所需的形状, 这会导致复杂的变换。

更糟的是,在可能在表单输入中遇到命名冲突的情况下,您可能会发现自己为了语义而使用长而笨拙的名称。

 <form>
  <fieldset>
    <legend>Contact</legend>
    <label for="contact_first-name">First Name</label>
    <input type="text" name="contact_first-name" id="contact_first-name">
    <label for="contact_last-name">Last Name</label>
    <input type="text" name="contact_last-name" id="contact_last-name">
    <label for="contact_email">Email</label>
    <input type="email" name="contact_email" id="contact_email">
    <label for="contact_phone">Phone</label>
    <input type="text" name="contact_phone" id="contact_phone">
  </fieldset>
  <!-- ... -->
</form>

表单处理程序必须将该数据转换为API期望的表单。 幸运的是,这是Angular 2有一个解决方案。

ngModelGroup

当在Angular 2中构建模板驱动的表单时,我们可以依靠ngModelGroup指令来实现一个更干净的实现,这样Angular就会将表单字段转换为嵌套数据。

<form #paymentForm="ngForm" (ngSubmit)="purchase(paymentForm)">
  <fieldset ngModelGroup="contact">
    <legend>Contact</legend>
    <label>
      First Name <input type="text" name="firstname" ngModel>
    </label>
    <label>
      Last Name <input type="text" name="lastname" ngModel>
    </label>
    <label>
      Email <input type="email" name="email" ngModel>
    </label>
    <label>
      Phone <input type="text" name="phone" ngModel>
    </label>
  </fieldset>
  <fieldset ngModelGroup="address">
    <!-- ... -->
  </fieldset>
  <fieldset ngModelGroup="paymentCard">
    <!-- ... -->
  </fieldset>
</form>
  • 使用替代HTML5标签格式; ID与ngForm / ngModelparadigm没有关系
  • 除了语义目的,ngModelGroup不必在<fieldset>上使用,它会和放在<div>上一样。

如果我们填写表单,它将以我们需要的API的形式结束,而且仍然可以依靠HTML进行验证。

单向绑定

如果您需要具有默认值的表单,可以开始使用ngModel的值绑定语法。app/signup-form.component.html

<form #signupForm="ngForm" (ngSubmit)=register(signupForm)>
  <label for="username">Username</label>
  <input type="text" name="username" id="username" [ngModel]="generatedUser">
  <label for="email">Email</label>
  <input type="email" name="email" id="email" ngModel>
  <button type="submit">Sign Up</button>
</form>

app/signup-form.component.ts

import { Component } from '@angular/core';
import { NgForm } from '@angular/forms';
// ...
@Component({ /* ... */ })
export class SignupForm {
  generatedUser: string = generateUniqueUserID();
  register(form: NgForm) {
    console.log(form.value);
    // ...
  }
}

双向绑定

虽然Angular 2默认情况下假定单向绑定,但如果需要,双向绑定仍然可用。为了能够访问模板驱动表单中的双向绑定,请使用“Banana-Box”语法([(ngModel)] ="propertyName")。请务必声明组件上需要的所有属性。

<form #signupForm="ngForm" (ngSubmit)=register(signupForm)>
  <label for="username">Username</label>
  <input type="text" name="username" id="username" [(ngModel)]="username">
  <label for="email">Email</label>
  <input type="email" name="email" id="email" [(ngModel)]="email">
  <button type="submit">Sign Up</button>
</form>
import { Component } from '@angular/core';
import { NgForm } from '@angular/forms';
// ...
@Component({ /* ... */ })
export class SignUpForm {
  username: string = generateUniqueUserID();
  email: string = '';
  register(form: NgForm) {
    console.log(form.value.username);
    console.log(this.username);
    // ...
  }
}

验证模板驱动的表单

验证

验证模板驱动的表单,下面是使用HTML5的做法:

<!-- a required field -->
<input type="text" required>
<!-- an optional field of a specific length -->
<input type="text" pattern=".{3,8}">
<!-- a non-optional field of specific length -->
<input type="text" pattern=".{3,8}" required>
<!-- alphanumeric field of specific length -->
<input type="text" pattern="[A-Za-z0-9]{0,5}">

请注意,pattern属性是JavaScript RegEx语法的一个不太强大的版本。还有其他HTML5属性,可以学习和应用于各种类型的输入; 然而在大多数情况下,它们作为上限和下限,防止添加或删除额外信息。

<!-- a field which will accept no more than 5 characters -->
<input type="text" maxlength="5">

在编写模板驱动表单时,可以使用这两种方法之一。 关注用户体验:在某些情况下,防止意外输入是有意义的,在其他情况下,允许不受限制的输入是有意义的,但提供类似计数器的东西以显示限制。

下一节:在我们的模板中使用指令给我们没有太多的样板快速原型的能力,我们被束缚住了。 相反,响应式表单,让我们通过代码定义我们的形式,并给我们对数据验证更多的灵活性和控制。

首先,在它的简便性上有一点魔法,但是在你熟悉基础知识之后,学习它的构建块将允许你处理更复杂的用例。