GVKun编程网logo

angular reactive form

13

在这篇文章中,我们将带领您了解angularreactiveform的全貌,同时,我们还将为您介绍有关Angular2:ReactiveForm更新模型、Angular4ReactiveForms切换

在这篇文章中,我们将带领您了解angular reactive form的全貌,同时,我们还将为您介绍有关Angular 2:ReactiveForm更新模型、Angular 4 Reactive Forms切换隐藏表单元素的验证、Angular 4.x Reactive Forms、Angular 4.x中表单Reactive Forms详解的知识,以帮助您更好地理解这个主题。

本文目录一览:

angular reactive form

angular reactive form

这篇文章讲了angular reactive form,

 

这里是angular file upload 组件 https://malcoded.com/posts/angular-file-upload-component-with-express/ 

 

原文:https://malcoded.com/posts/angular-fundamentals-reactive-forms/

------------------------------------

Forms are arguably one point where angular really shines.

So learning how forms work in angular is an essential skill.

In this tutorial we are going to take a close look at one of the two ways to create forms in angular. The reactive forms.

We will dive right into code and discover all the details about reactive forms in the angular framework step by step and with easy examples.

So, without further ado, let’s get started!

angular-forms-banner

Forms in Angular

There are two different approaches to forms in Angular.

The first category are the template-driven forms. Using this method, you first create html-input-elements and then use directives like ngModel to bind their value to a component’s variable.

Personally, I’ve used this technique a lot. But that way only because I did not kNow about the other category.

Reactive Forms.

Reactive Forms a kind of the opposite to template-driven forms. Instead of defining the form in your template, the structure of the form is defined in code.

This maybe sounds a little bit odd Now. At least I felt like this.

“Isn’t it twice the work, to define the form in code?”

“And isn’t it much harder to read?”

That was what I asked myself and that is what you should ask yourself, too!

But please wait with that, until you read this article and fully understand the topic.

To answer these questions, let’s dive right in and just build a small example using reactive forms together!

angular-modules-banner

Importing the Module

For this example we are going to use a blank angular-cli application.

I you haven’t done so already, please go ahead and create one:

ng new [name]

As you probably kNow, Angular is organized in modules.

To use certain features, we first need to import the modules that contain that feature, before we can use it.

This is true for reactive forms, as well.

To use reactive forms, we need to import the ReactiveFormsModule into our parent module.

In our case, the parent module is the AppModule.

Let’s import the required module into it:

src/app.module.ts
import { browserModule } from ''@angular/platform-browser''
import { NgModule } from ''@angular/core''
import { ReactiveFormsModule } from ''@angular/forms''

import { AppComponent } from ''./app.component''

@NgModule({
  declarations: [AppComponent],
  imports: [browserModule, ReactiveFormsModule],
  providers: [],
  bootstrap: [AppComponent],
})
class AppModule {}

angular-code-banner

Defining a Model

In this example we are going to create a simple contact-form, because I Couldn’t think of something more creative.

Before we start creating our form, we first need to define, which information we would like to gather with it.

To do so, we are creating a so called model.

A model is just a class, that contains all the fields, that our data can have.

All we need to do, is to create a new file for that class. Also, I typically create a new folder called models to contain all models of the application.

Inside of that file, we create our model. In this case, the data will be split in two parts.

Personal data about the person using the contact-form and the actual data he/she wants to submit.

The model for that looks like this:

src/models/contact-request.ts
export class ContactRequest {
  personalData: PersonalData
  requestType: any = ''''
  text: string = ''''
}

class PersonalData {
  email: string = ''''
  mobile: string = ''''
  country: string = ''''
}

As you can see, I have also added default values to each field, as we will require them later.

angular-new-banner

Setting up a Contact-Component

Next, we need a component, to create our form in.

So let’s just create one!

Just use the following command of the angular-cli:

ng generate component contact

This will create a basic component for us to use. It will also import that component into our AppModule automatically.

angular-animation-banner

Creating a Reactive Form in Angular

Now we can finally start creating our form.

Other than with template-driven forms, we can not simply use the model we have created earlier.

Instead we have to create a different one. One, that is only responsible for the form-representation.

But why doe we have to do that? Isn’t that much more complex that just binding to the model ?

Of course it is.

But binding to the model directly comes with one large downside.

We are altering the original data directly.

Reactive Programming and Immutability

This has become bad-practice in the last couple years, as a paradigm called reactive programming took the programming world over more and more.

Why?

Well, the simplest answer might be, that we loose the original data. If the user decides to abort his action, we can not recover the prevIoUs state, as we have overwritten it.

What we do with reactive forms instead, is keeping the data in the form model, until the user hits the submit button.

Only then the data is copied over to the original model, replacing everything. This type of overwriting everything is called “immutable objects”.

Form Controls

To create this form-model, angular uses a class called FormGroup.

To define our form-model, we create a new FormGroup. The constructor of this class then takes an object, that can contain sub-form-groups and FormControls.

FormControls represent the leafs in this tree. They are the smallest possible unit and typically represent a HTML-control (input, select, …) of your template.

In actuall code, let’s create a new method, that creates a new instance of the form-model for us. We call this mehtod: createFormGroup.

src/contact/contact.component.ts
createFormGroup() {
  return new FormGroup({
    personalData: new FormGroup({
      email: new FormControl(),
      mobile: new FormControl(),
      country: new FormControl()
     }),
    requestType: new FormControl(),
    text: new FormControl()
  });
}

You will notice the similarities with our contact-request model.

In fact, I would recommend that you always keep them that similar. That way, you wont have any trouble converting the form-model to your actual model.

Because we want the country and the request Type to be select-able as a drop-down, we also have to provide some options to choose from:

src/contact/contact.component.ts
countries = [''USA'', ''Germany'', ''Italy'', ''France'']

requestTypes = [''Claim'', ''Feedback'', ''Help Request'']

Finally, we save the result of that method to a public field, to be accessible in our template.

src/contact/contact.component.ts
contactForm: FormGroup;
constructor() {
    this.contactForm = this.createFormGroup();
}

Here is how your component should look like Now:

src/contact/contact.component.ts
import { Component, OnInit } from ''@angular/core''
import { FormControl, FormGroup } from ''@angular/forms''

@Component({
  selector: ''app-contact'',
  templateUrl: ''./contact.component.html'',
  styleUrls: [''./contact.component.css''],
})
class ContactComponent implements OnInit {
  contactForm: FormGroup

  countries = [''USA'', ''Germany'', ''Italy'', ''France'']

  requestTypes = [''Claim'', ''Feedback'', ''Help Request'']

  constructor() {
    this.contactForm = this.createFormGroup()
  }

  // Step 1
  createFormGroup() {
    return new FormGroup({
      personalData: new FormGroup({
        email: new FormControl(),
        mobile: new FormControl(),
        country: new FormControl(),
      }),
      requestType: new FormControl(),
      text: new FormControl(),
    })
  }

  ngOnInit() {}
}

angular-ui-banner

Implementing the Template

For Now that is all we need to do code-wise.

Let’s take a look how the counterpart, the template will look like.

Basically, our HTML does look just like a regular form. The only difference is, that we have to tell angular, which FormGroup and which FormControl to use for each control.

To see the result, I’ve also added a paragraph to print out our form-model as json.

src/contact/contact.component.html
<form [formGroup]="contactForm" (ngSubmit)="onSubmit()" novalidate>
  <div formGroupName="personalData" novalidate>
    <input formControlName="email" /> <input formControlName="mobile" />
    <select formControlName="country">
      <option *ngFor="let country of countries" [value]="country"
        >{{country}}</option
      >
    </select>
  </div>
  <select formControlName="requestType">
    <option *ngFor="let requestType of requestTypes" [value]="requestType"
      >{{requestType}}</option
    >
  </select>
  <input formControlName="text" />
  <button type="submit" [disabled]="contactForm.pristine">Save</button>
  <button type="reset" (click)="revert()" [disabled]="contactForm.pristine">
    Revert
  </button>
</form>

At this point, your app should be in a runable state again.

In case you didn’t kNow: You can start your application using the

ng serve

command.

angular-build-banner

Using the Angular Form-Builder

From the example of our TypeScript code above, we can say that the code is starting to look cluttered already.

To solve that, angular provides a service called FormBuilder. This FormBuilder allows us to build our form-model with less code.

To use the FormBuilder, we need to request it via dependency injection. We do so in the constructor of our component.

src/contact/contact.component.ts
constructor(private formBuilder: FormBuilder) {
    this.contactForm = this.createFormGroup();
}

The FormBuilder class has to be imported from @angular/forms:

src/contact/contact.component.ts
import { FormControl, FormGroup, FormBuilder } from ''@angular/forms''

Now we can use that FromBuilder to build our form-model. For demonstration-purposes, I have created a new method called “createFormGroupWithBuilder”. You can just replace you prevIoUs code if you want to.

src/contact/contact.component.ts
createFormGroupWithBuilder(formBuilder: FormBuilder) {
    return formBuilder.group({
      personalData: formBuilder.group({
        email: ''defaul@email.com'',
        mobile: '''',
        country: ''''
      }),
      requestType: '''',
      text: ''''
    });
  }

Using this approach, we have to define the default values of each field. You can just pass them an empty string, or fill it with some default-data as I did with the “email”-field.

Passing a Class to the Form-Builder

I turns out, using the FormBuilder, we can further optimize our code.

Instead of defining our form-model inline, the FormBuilder allows us to pass in any JavaScript object.

So we can just pass in a new instance of our PersonalData class.

src/contact/contact.component.ts
createFormGroupWithBuilderAndModel(formBuilder: FormBuilder) {
    return formBuilder.group({
      personalData: formBuilder.group(new PersonalData()),
      requestType: '''',
      text: ''''
    });
  }

However, we have to make sure, that all fields of the form are also present in this object. This is why we have assigned the default values in our contact-request-model.

angular-test-banner

Extracting the Data from the Form

Now that have talked about our more than enough, it’s time to worry about getting our data out of that form into an contact-request object.

For that, we are using the submit-button, we have created earlier in our template. We also already registered the callback-method “onSubmit”.

To be able to listen to the button-press, we need to implement that callback in our component.

Inside of that callback, we can access the form’s values by its values property.

this.contactForm.value

But we need to be careful here. These values are still referenced by our form. If we just copy that reference and modify the data elsewhere, the form will be impacted, as well. This can cause weird side-effects.

This is why we need to create a copy of the data. In this example we are using Object.assign for that.

const result: ContactRequest = Object.assign({}, this.contactForm.value);

However, that is not enough. Object.assign creates something called a shallow copy. That means that all primary-fields of the object are copied properly, but the references to sub-objects or lists are still the same. Not noticing this problem does cause even more weird side-effects.

Instead what we want to do is create a deep copy. We can either achieve that by doing it copying everything manually or use a utility like lodash’s cloneDeep function. We will use the manual method here.

result.personalData = Object.assign({}, result.personalData);

Overall, the onSubmit method should Now look something like this:

src/contact/contact.component.ts
onSubmit() {
    // Make sure to create a deep copy of the form-model
    const result: ContactRequest = Object.assign({}, this.contactForm.value);
    result.personalData = Object.assign({}, result.personalData);

    // Do useful stuff with the gathered data
    console.log(result);
  }

angular-healing-banner

Resetting the Form

Resetting the form is definitely one of the easier parts with reactive forms.

Again, we are going to use the reset-button, we have included into our template already.

All we need to do Now, is to implement the revert-callback into our component.

To reset the form, we can either use the form’s reset method without any parameter,

// Resets to blank object
this.contactForm.reset();

which results in an empty object, or pass a set of default parameters along:

// Resets to provided model
this.contactForm.reset({ personalData: new PersonalData(), requestType: '''', text: '''' });

All in all the revert method simply looks like this:

src/contact/contact.component.ts
revert() {
    // Resets to blank object
    this.contactForm.reset();

    // Resets to provided model
    this.contactForm.reset({ personalData: new PersonalData(), requestType: '''', text: '''' });
  }

Conclusion

In this tutorial we learned how to create forms with angular using the reactive-forms method.

I hope you enjoyed this post.

If you did please hit the share button below and help other people understand reactive-forms in angular, as well.

Have a fantastic day!

总结

以上是小编为你收集整理的angular reactive form全部内容。

如果觉得小编网站内容还不错,欢迎将小编网站推荐给好友。

原文地址:https://www.cnblogs.com/oxspirt/p/10863863.html

Angular 2:ReactiveForm更新模型

Angular 2:ReactiveForm更新模型

我有一个反应形式,我想直接填充我的模型.

form.component.html

<form [formGroup]="personForm" (ngSubmit)="savePerson()">
    <md-card>
        <md-input formControlName="personName" placeholder="Person Name"></md-input>
        ... many more fields
        <button md-raised-button color="primary">Save</button>
    </md-card>
</form>

form.component.ts

@Component({
    selector: 'person',template: 'form.component.html'
})

export class FormComponent implements OnInit {

    person: Person;
    formBuilder: FormBuilder;
    personForm: FormGroup;

    constructor(formBuilder: FormBuilder,personService: PersonService) {
        this.person = new Person();
        this.personForm = formBuilder.group({
            personName: [this.person.personName,Validators.required],... many more properties for form
        });
    }

    ngOnInit() {
        console.log('ngOnInit() called');
    }

    savePerson() {
        this.person.personName = this.personForm.value.personName;
        ... many more properties set
        this.personService.savePersontoDB(this.person);
    }
}

在savePerson()函数中,我不得不将personForm FormGroup中的值复制到Person对象.对于少数几个属性,这很好,但如果我有很多属性,这将是另一件需要管理的事情.如何简化此代码以便:

>当表单值更改时,personForm值将自动复制到Person对象.
>当用户按下“保存”按钮(随后调用save()函数)时,personForm值会自动复制到Person对象.这样,我的意思是不必将所有单独的表单值复制到我的模型中(Person) )对象.

实现这一目标的最佳方法是什么?我可以使用某种助手还是比这更简单?

非常感谢

JT

解决方法

在我的应用程序中,我做到了这一点:

this.selectedPhysical = <Physical>this.physicalForm.value;

这将ui形式的字段映射到底层对象.所以你可以:

this.person = <Person>this.personForm.value;
this.personService.savePersontoDB(this.person);

Angular 4 Reactive Forms切换隐藏表单元素的验证

Angular 4 Reactive Forms切换隐藏表单元素的验证

我有一个反应形式,在负载时不需要任何字段.如果选择了一个选项,将其他表单元素添加到formGroup中,则所有新显示的字段都是必需的.
如果昵称字段被隐藏,那么您应该能够提交表格.如果显示昵称,则需要昵称字段,并且在昵称字段已满之前禁用提交按钮.
这是我想要做的一个例子.

我的问题是,一旦显示/隐藏表单元素,如何启用/禁用验证?

App.module.ts

import { NgModule } from '@angular/core';
import { browserModule } from '@angular/platform-browser';
import { FormsModule,ReactiveFormsModule } from '@angular/forms';

import { AppComponent } from './app.component';
import { HelloComponent } from './hello.component';

@NgModule({
  imports:      [ browserModule,FormsModule,ReactiveFormsModule ],declarations: [ AppComponent,HelloComponent ],bootstrap:    [ AppComponent ]
})
export class AppModule { }

App.component.ts

import { Component,OnInit } from '@angular/core';
import { Validators,FormControl,FormGroup,FormBuilder } from '@angular/forms';

@Component({
  selector: 'my-app',templateUrl: './app.component.html',styleUrls: [ './app.component.css' ]
})
export class AppComponent implements OnInit  {
  name = 'My Reactive Form';

  constructor(
    private fb: FormBuilder
  ) {}

  myForm: FormGroup;
  showNick: boolean = false;

  ngOnInit() {
    this.myForm = this.fb.group({
      'firstName': new FormControl(),'nickName': new FormControl('',Validators.required),'lastName': new FormControl()
    })
  }

  toggleNick() {
    this.showNick = !this.showNick;
  }
}

app.component.html

<form [formGroup]="myForm">
  <divhttps://www.jb51.cc/tag/Box/" target="_blank">Box">
    <label>
      First Name
      <input type="text" formControlName="firstName">
    </label>
  </div>
  <divhttps://www.jb51.cc/tag/Box/" target="_blank">Box nickname">
    Nickname? &nbsp; <a (click)="toggleNick()">yes / no</a>
  </div>
  <divhttps://www.jb51.cc/tag/Box/" target="_blank">Box" *ngIf="showNick">
    <label>
      Nickname
      <input type="text" formControlName="nickName">
      <span*ngIf="!myForm.controls['nickName'].valid && myForm.controls['nickName'].dirty">
    This field is invalid
  </span>
    </label>
  </div>
  <divhttps://www.jb51.cc/tag/Box/" target="_blank">Box">
    <label>
      Last Name
      <input type="text" formControlName="lastName">
    </label>
  </div>
  <button [disabled]="myForm.invalid">Submit</button>
</form>

解决方法

在我的申请中,我有类似的要求.如果用户要求通过文本通知,则需要电话.否则电话号码是可选的.

我写了这个方法:

setNotification(notifyVia: string): void {
    const phoneControl = this.customerForm.get('phone');
    if (notifyVia === 'text') {
        phoneControl.setValidators(Validators.required);
    } else {
        phoneControl.clearValidators();
    }
    phoneControl.updateValueAndValidity();
}

它是从这个代码中调用的,它位于ngOnInit中:

this.customerForm.get('notification').valueChanges
                     .subscribe(value => this.setNotification(value));

如果用户更改了通知字段(单选按钮),则会调用传入值的setNotification方法.如果值是’text’通知,则会将手机的验证设置为required.

否则它会清除手机字段的验证.

然后它必须调用updateValueAndValidity以使用此新验证更新表单信息.

Angular 4.x Reactive Forms

Angular 4.x Reactive Forms

Angular 4.x 中有两种表单:

  • Template-Driven Forms - 模板驱动式表单 (类似于 AngularJS 1.x 中的表单 )

  • Reactive Forms (Model-Driven Forms) - 响应式表单

Template-Driven Forms (模板驱动表单) ,我们之前的文章已经介绍过了,了解详细信息,请查看 - Angular 4.x Template-Driven Forms 。

Contents

  • ngModule and reactive forms

  • FormControl and FormGroup

  • Implementing our FormGroup model

  • Binding our FormGroup model

  • Reactive submit

  • Reactive error validation

  • Simplifying with FormBuilder

Form base and interface

Form base

<form novalidate>
  <label>
    <span>Full name</span>
    <input
      type="text"
      name="name"
      placeholder="Your full name">
  </label>
  <div>
    <label>
      <span>Email address</span>
      <input
        type="email"
        name="email"
        placeholder="Your email address">
    </label>
    <label>
      <span>Confirm address</span>
      <input
        type="email"
        name="confirm"
        placeholder="Confirm your email address">
    </label>
  </div>
  <button type="submit">Sign up</button>
</form>

接下来我们要实现的功能如下:

  • 绑定 name、email、confirm 输入框的值

  • 为所有输入框添加表单验证功能

  • 显示验证异常信息

  • 表单验证失败时,不允许进行表单提交

  • 表单提交功能

User interface

// signup.interface.ts
export interface User {
  name: string;
  account: {
    email: string;
    confirm: string;
  }
}

ngModule and reactive forms

在我们继续深入介绍 reactive forms 表单前,我们必须在 @NgModule 中导入 @angular/forms 库中的 ReactiveFormsModule

import { ReactiveFormsModule } from '@angular/forms';

@NgModule({
  imports: [
    ...,ReactiveFormsModule
  ],declarations: [...],bootstrap: [...]
})
export class AppModule {}

友情提示:若使用 reactive forms,则导入 ReactiveFormsModule;若使用 template-driven 表单,则导入 FormsModule。

Reactive approach

我们将基于上面的定义的基础表单,创建 SignupFormComponent

signup-form.component.ts

import { Component } from '@angular/core';

@Component({
  selector: 'signup-form',template: `
    <form novalidate>...</form>
  `
})
export class SignupFormComponent {
  constructor() {}
}

这是一个基础的组件,在我们实现上述功能前,我们需要先介绍 FormControlFormGroupFormBuilder 的概念和使用。

FormControl and FormGroup

我们先来介绍一下 FormControl 和 FormGroup 的概念:

  • FormControl - 它是一个为单个表单控件提供支持的类,可用于跟踪控件的值和验证状态,此外还提供了一系列公共API。

使用示例:

ngOnInit() {
  this.myControl = new FormControl('Semlinker');
}
  • FormGroup - 包含是一组 FormControl 实例,可用于跟踪 FormControl 组的值和验证状态,此外也提供了一系列公共API。

使用示例:

ngOnInit() {
  this.myGroup = new FormGroup({
    name: new FormControl('Semlinker'),location: new FormControl('China,CN')
  });
}

现在我们已经创建了 FormControlFormGroup 实例,接下来我们来看一下如何使用:

<form novalidate [formGroup]="myGroup">
  Name: <input type="text" formControlName="name">
  Location: <input type="text" formControlName="location">
</form>

注意事项:Template-Driven Forms 中介绍的 ngModelname="" 属性,已经被移除了。这是一件好事,让我们的模板更简洁。

上面示例中,我们必须使用 [formGroup] 绑定我们创建的 myGroup 对象,除此之外还要使用 formControlName 指令,绑定我们创建的 FormControl 控件。此时的表单结构如下:

FormGroup -> 'myGroup'
    FormControl -> 'name'
    FormControl -> 'location'

Implementing our FormGroup model

signup.interface.ts

export interface User {
  name: string;
  account: {
    email: string;
    confirm: string;
  }
}

与之对应的表单结构如下:

FormGroup -> 'user'
    FormControl -> 'name'
    FormGroup -> 'account'
        FormControl -> 'email'
        FormControl -> 'confirm'

是的,我们可以创建嵌套的 FormGroup 集合!让我们更新一下组件 (不包含初始数据):

import { Component,OnInit } from '@angular/core';
import { FormControl,FormGroup } from '@angular/forms';

@Component({...})
export class SignupFormComponent implements OnInit {
  user: FormGroup;
  ngOnInit() {
    this.user = new FormGroup({
      name: new FormControl(''),account: new FormGroup({
        email: new FormControl(''),confirm: new FormControl('')
      })
    });
  }
}

如果我们想要设置初始数据,我们可以按照上述示例进行设置。通常情况下,我们通过服务端提供的 API 接口来获取表单的初始信息。

Binding our FormGroup model

现在我们已经实例化了 FormGroup 模型,是时候绑定到对应的 DOM 元素上了。具体示例如下:

<form novalidate [formGroup]="user">
  <label>
    <span>Full name</span>
    <input
      type="text"
      placeholder="Your full name"
      formControlName="name">
  </label>
  <div formGroupName="account">
    <label>
      <span>Email address</span>
      <input
        type="email"
        placeholder="Your email address"
        formControlName="email">
    </label>
    <label>
      <span>Confirm address</span>
      <input
        type="email"
        placeholder="Confirm your email address"
        formControlName="confirm">
    </label>
  </div>
  <button type="submit">Sign up</button>
</form>

现在 FormGroupFormControl 对象与 DOM 结构的关联信息如下:

// JavaScript APIs
FormGroup -> 'user'
    FormControl -> 'name'
    FormGroup -> 'account'
        FormControl -> 'email'
        FormControl -> 'confirm'

// DOM bindings
formGroup -> 'user'
    formControlName -> 'name'
    formGroupName -> 'account'
        formControlName -> 'email'
        formControlName -> 'confirm'

当使用模板驱动的表单时,为了获取 f.value 表单的值,我们需要先执行 #f="ngForm" 的操作。而对于使用响应式的表单,我们可以通过以下方式,方便的获取表单的值:

{{ user.value | json }} // { name: '',account: { email: '',confirm: '' }}

Reactive submit

跟模板驱动的表单一样,我们可以通过 ngSubmit 输出属性,处理表单的提交逻辑:

<form novalidate (ngSubmit)="onSubmit(user)" [formGroup]="user">
  ...
</form>

需要注意的是,我们使用 user 对象作为 onSubmit() 方法的参数,这使得我们可以获取表单对象的相关信息,具体处理逻辑如下:

export class SignupFormComponent {
  user: FormGroup;
  onSubmit({ value,valid }: { value: User,valid: boolean }) {
    console.log(value,valid);
  }
}

上面代码中,我们使用 Object destructuring (对象解构) 的方式,从user 对象中获取 valuevalid 属性的值。其中 value 的值,就是 user.value 的值。在实际应用中,我们是不需要传递 user 参数的:

export class SignupFormComponent {
  user: FormGroup;
  onSubmit() {
    console.log(this.user.value,this.user.valid);
  }
}

表单的数据绑定方式和提交逻辑已经介绍完了,是该介绍表单实际应用中,一个重要的环节 — 表单验证。

Reactive error validation

接下来我们来为表单添加验证规则,首先我们需要从 @angular/forms 中导入 Validators。具体使用示例如下:

ngOnInit() {
  this.user = new FormGroup({
    name: new FormControl('',[Validators.required,Validators.minLength(2)]),account: new FormGroup({
      email: new FormControl('',Validators.required),confirm: new FormControl('',Validators.required)
    })
  });
}

通过以上示例,我们可以看出,如果表单控制包含多种验证规则,可以使用数组声明多种验证规则。若只包含一种验证规则,直接声明就好。通过这种方式,我们就不需要在模板的输入控件中添加 required 属性。接下来我们来添加表单验证失败时,不允许进行表单提交功能:

<form novalidate (ngSubmit)="onSubmit(user)" [formGroup]="user">
  ...
  <button type="submit" [disabled]="user.invalid">Sign up</button>
</form>

那么问题来了,我们要如何获取表单控件的验证信息?我们可以使用模板驱动表单中介绍的方式,具体如下:

<form novalidate [formGroup]="user">
  {{ user.controls.name?.errors | json }}
</form>

友情提示:?.prop 称为安全导航操作符,用于告诉 Angular prop 的值可能不存在。

此外我们也可以使用 FormGroup 对象提供的 API,来获取表单控件验证的错误信息:

<form novalidate [formGroup]="user">
  {{ user.get('name').errors | json }}
</form>

现在我们来看一下完整的代码:

import { Component,FormGroup,Validators } from '@angular/forms';
import { User } from './signup.interface';

@Component({
  selector: 'signup-form',template: `
    <form novalidate (ngSubmit)="onSubmit(user)" [formGroup]="user">
      <label>
        <span>Full name</span>
        <input type="text" placeholder="Your full name" formControlName="name">
      </label>
      <div*ngIf="user.get('name').hasError('required') && 
            user.get('name').touched">
        Name is required
      </div>
      <div*ngIf="user.get('name').hasError('minlength') && 
            user.get('name').touched">
        Minimum of 2 characters
      </div>
      <div formGroupName="account">
        <label>
          <span>Email address</span>
          <input type="email" placeholder="Your email address" formControlName="email">
        </label>
        <div*ngIf="user.get('account').get('email').hasError('required') && 
             user.get('account').get('email').touched">
          Email is required
        </div>
        <label>
          <span>Confirm address</span>
          <input type="email" placeholder="Confirm your email address" 
             formControlName="confirm">
        </label>
        <div*ngIf="user.get('account').get('confirm').hasError('required') && 
             user.get('account').get('confirm').touched">
          Confirming email is required
        </div>
      </div>
      <button type="submit" [disabled]="user.invalid">Sign up</button>
    </form>
  `
})
export class SignupFormComponent implements OnInit {
  user: FormGroup;
  constructor() {}
  ngOnInit() {
    this.user = new FormGroup({
      name: new FormControl('',account: new FormGroup({
        email: new FormControl('',Validators.required)
      })
    });
  }
  onSubmit({ value,valid);
  }
}

功能是实现了,但创建 FormGroup 对象的方式有点繁琐,Angular 团队也意识到这点,因此为我们提供 FormBuilder ,来简化上面的操作。

Simplifying with FormBuilder

首先我们需要从 @angular/forms 中导入 FormBuilder

import { FormBuilder,Validators } from '@angular/forms';

export class SignupFormComponent implements OnInit {
  user: FormGroup;
  constructor(private fb: FormBuilder) {}
  ...
}

然后我们使用 FormBuilder 对象提供的 group() 方法,来创建 FormGroupFormControl 对象:

调整前的代码 (未使用FormBuilder):

ngOnInit() {
  this.user = new FormGroup({
    name: new FormControl('',Validators.required)
    })
  });
}

调整后的代码 (使用FormBuilder):

ngOnInit() {
  this.user = this.fb.group({
    name: ['',Validators.minLength(2)]],account: this.fb.group({
      email: ['',Validators.required],confirm: ['',Validators.required]
    })
  });
}

对比一下调整前和调整后的代码,是不是感觉一下子方便了许多。此时更新完后完整的代码如下:

@Component({...})
export class SignupFormComponent implements OnInit {
  user: FormGroup;
  constructor(private fb: FormBuilder) {}
  ngOnInit() {
    this.user = this.fb.group({
      name: ['',account: this.fb.group({
        email: ['',Validators.required]
      })
    });
  }
  onSubmit({ value,valid);
  }
}

我有话说

Template-Driven Forms vs Reactive Forms

Template-Driven Forms (模板驱动表单) 的特点

  • 使用方便

  • 适用于简单的场景

  • 通过 [(ngModel)] 实现数据双向绑定

  • 最小化组件类的代码

  • 不易于单元测试

Reactive Forms (响应式表单) 的特点

  • 比较灵活

  • 适用于复杂的场景

  • 简化了HTML模板的代码,把验证逻辑抽离到组件类中

  • 方便的跟踪表单控件值的变化

  • 易于单元测试

参考资源

  • Angular 2 form fundamentals: reactive forms

Angular 4.x中表单Reactive Forms详解

Angular 4.x中表单Reactive Forms详解

Angular 4.x 中有两种表单:

  • Template-Driven Forms - 模板驱动式表单 (类似于 Angular 1.x 中的表单 )
  • Reactive Forms (Model-Driven Forms) - 响应式表单

Template-Driven Forms (模板驱动表单) ,我们之前的文章已经介绍过了,了解详细信息,请查看 - Angular 4.x Template-Driven Forms 。

Contents

  • ngModule and reactive forms
  • FormControl and FormGroup
  • Implementing our FormGroup model
  • Binding our FormGroup model
  • Reactive submit
  • Reactive error validation
  • Simplifying with FormBuilder

Form base and interface

Form base

<form novalidate>
 <label>
 <span>Full name</span>
 <input
 type="text"
 name="name"
 placeholder="Your full name">
 </label>
 <div>
 <label>
 <span>Email address</span>
 <input
 type="email"
 name="email"
 placeholder="Your email address">
 </label>
 <label>
 <span>Confirm address</span>
 <input
 type="email"
 name="confirm"
 placeholder="Confirm your email address">
 </label>
 </div>
 <button type="submit">Sign up</button>
</form>

接下来我们要实现的功能如下:

  • 绑定 name、email、confirm 输入框的值
  • 为所有输入框添加表单验证功能
  • 显示验证异常信息
  • 表单验证失败时,不允许进行表单提交
  • 表单提交功能

User interface

// signup.interface.ts
export interface User {
 name: string;
 account: {
 email: string;
 confirm: string;
 }
}

ngModule and reactive forms

在我们继续深入介绍 reactive forms 表单前,我们必须在 @NgModule 中导入 @angular/forms 库中的 ReactiveFormsModule:

import { ReactiveFormsModule } from ''@angular/forms'';

@NgModule({
 imports: [
 ...,
 ReactiveFormsModule
 ],
 declarations: [...],
 bootstrap: [...]
})
export class AppModule {}

友情提示:若使用 reactive forms,则导入 ReactiveFormsModule;若使用 template-driven 表单,则导入 FormsModule。

Reactive approach

我们将基于上面的定义的基础表单,创建 SignupFormComponent :

signup-form.component.ts

import { Component } from ''@angular/core'';

@Component({
 selector: ''signup-form'',
 template: `
 <form novalidate>...</form>
 `
})
export class SignupFormComponent {
 constructor() {}
}

这是一个基础的组件,在我们实现上述功能前,我们需要先介绍 FormControl、FormGroup、FormBuilder 的概念和使用。

FormControl and FormGroup

我们先来介绍一下 FormControl 和 FormGroup 的概念:

1、FormControl - 它是一个为单个表单控件提供支持的类,可用于跟踪控件的值和验证状态,此外还提供了一系列公共API。
使用示例:

ngOnInit() {
 this.myControl = new FormControl(''Semlinker'');
}

2、FormGroup - 包含是一组 FormControl 实例,可用于跟踪 FormControl 组的值和验证状态,此外也提供了一系列公共API。

使用示例:

ngOnInit() {
 this.myGroup = new FormGroup({
 name: new FormControl(''Semlinker''),
 location: new FormControl(''China, CN'')
 });
}

现在我们已经创建了 FormControl 和 FormGroup 实例,接下来我们来看一下如何使用:

<form novalidate [formGroup]="myGroup">
 Name: <input type="text" formControlName="name">
 Location: <input type="text" formControlName="location">
</form>

注意事项:Template-Driven Forms 中介绍的 ngModel 和 name="" 属性,已经被移除了。这是一件好事,让我们的模板更简洁。

上面示例中,我们必须使用 [formGroup] 绑定我们创建的 myGroup 对象,除此之外还要使用 formControlName 指令,绑定我们创建的 FormControl 控件。

此时的表单结构如下:

FormGroup -> ''myGroup''
 FormControl -> ''name''
 FormControl -> ''location''

Implementing our FormGroup model

signup.interface.ts

export interface User {
 name: string;
 account: {
 email: string;
 confirm: string;
 }
}

与之对应的表单结构如下:

FormGroup -> ''user''
 FormControl -> ''name''
 FormGroup -> ''account''
 FormControl -> ''email''
 FormControl -> ''confirm''

是的,我们可以创建嵌套的 FormGroup 集合!让我们更新一下组件 (不包含初始数据):

import { Component, OnInit } from ''@angular/core'';
import { FormControl, FormGroup } from ''@angular/forms'';

@Component({...})
export class SignupFormComponent implements OnInit {
 user: FormGroup;
 ngOnInit() {
 this.user = new FormGroup({
 name: new FormControl(''''),
 account: new FormGroup({
 email: new FormControl(''''),
 confirm: new FormControl('''')
 })
 });
 }
}

如果我们想要设置初始数据,我们可以按照上述示例进行设置。通常情况下,我们通过服务端提供的 API 接口来获取表单的初始信息。

Binding our FormGroup model

现在我们已经实例化了 FormGroup 模型,是时候绑定到对应的 DOM 元素上了。具体示例如下:

<form novalidate [formGroup]="user">
 <label>
 <span>Full name</span>
 <input
 type="text"
 placeholder="Your full name"
 formControlName="name">
 </label>
 <div formGroupName="account">
 <label>
 <span>Email address</span>
 <input
 type="email"
 placeholder="Your email address"
 formControlName="email">
 </label>
 <label>
 <span>Confirm address</span>
 <input
 type="email"
 placeholder="Confirm your email address"
 formControlName="confirm">
 </label>
 </div>
 <button type="submit">Sign up</button>
</form>

现在 FormGroup 与 FormControl 对象与 DOM 结构的关联信息如下:

// JavaScript APIs
FormGroup -> ''user''
 FormControl -> ''name''
 FormGroup -> ''account''
 FormControl -> ''email''
 FormControl -> ''confirm''

// DOM bindings
formGroup -> ''user''
 formControlName -> ''name''
 formGroupName -> ''account''
 formControlName -> ''email''
 formControlName -> ''confirm''

当使用模板驱动的表单时,为了获取 f.value 表单的值,我们需要先执行 #f="ngForm" 的操作。而对于使用响应式的表单,我们可以通过以下方式,方便的获取表单的值:

{{ user.value | json }} // { name: '''', account: { email: '''', confirm: '''' }}

Reactive submit

跟模板驱动的表单一样,我们可以通过 ngSubmit 输出属性,处理表单的提交逻辑:

<form novalidate (ngSubmit)="onSubmit(user)" [formGroup]="user">
 ...
</form>

需要注意的是:我们使用 user 对象作为 onSubmit() 方法的参数,这使得我们可以获取表单对象的相关信息,具体处理逻辑如下:

export class SignupFormComponent {
 user: FormGroup;
 onSubmit({ value, valid }: { value: User, valid: boolean }) {
 console.log(value, valid);
 }
}

上面代码中,我们使用 Object destructuring (对象解构) 的方式,从user 对象中获取 value 和 valid 属性的值。其中 value 的值,就是 user.value 的值。在实际应用中,我们是不需要传递 user 参数的:

export class SignupFormComponent {
 user: FormGroup;
 onSubmit() {
 console.log(this.user.value, this.user.valid);
 }
}

表单的数据绑定方式和提交逻辑已经介绍完了,是该介绍表单实际应用中,一个重要的环节 — 表单验证。

Reactive error validation

接下来我们来为表单添加验证规则,首先我们需要从 @angular/forms 中导入 Validators。具体使用示例如下:

ngOnInit() {
 this.user = new FormGroup({
 name: new FormControl('''', [Validators.required, Validators.minLength(2)]),
 account: new FormGroup({
 email: new FormControl('''', Validators.required),
 confirm: new FormControl('''', Validators.required)
 })
 });
}

通过以上示例,我们可以看出,如果表单控制包含多种验证规则,可以使用数组声明多种验证规则。若只包含一种验证规则,直接声明就好。通过这种方式,我们就不需要在模板的输入控件中添加 required 属性。接下来我们来添加表单验证失败时,不允许进行表单提交功能:

<form novalidate (ngSubmit)="onSubmit(user)" [formGroup]="user">
 ...
 <button type="submit" [disabled]="user.invalid">Sign up</button>
</form>

那么问题来了,我们要如何获取表单控件的验证信息?我们可以使用模板驱动表单中介绍的方式,具体如下:

<form novalidate [formGroup]="user">
 {{ user.controls.name?.errors | json }}
</form>

友情提示: ?.prop 称为安全导航操作符,用于告诉 Angular prop 的值可能不存在。

此外我们也可以使用 FormGroup 对象提供的 API,来获取表单控件验证的错误信息:

<form novalidate [formGroup]="user">
 {{ user.get(''name'').errors | json }}
</form>

现在我们来看一下完整的代码:

import { Component, OnInit } from ''@angular/core'';
import { FormControl, FormGroup, Validators } from ''@angular/forms'';
import { User } from ''./signup.interface'';

@Component({
 selector: ''signup-form'',
 template: `
 <form novalidate (ngSubmit)="onSubmit(user)" [formGroup]="user">
  <label>
  <span>Full name</span>
  <input type="text" placeholder="Your full name" formControlName="name">
  </label>
  <div*ngIf="user.get(''name'').hasError(''required'') && 
   user.get(''name'').touched">
  Name is required
  </div>
  <div*ngIf="user.get(''name'').hasError(''minlength'') && 
   user.get(''name'').touched">
  Minimum of 2 characters
  </div>
  <div formGroupName="account">
  <label>
   <span>Email address</span>
   <input type="email" placeholder="Your email address" formControlName="email">
  </label>
  <div*ngIf="user.get(''account'').get(''email'').hasError(''required'') && 
    user.get(''account'').get(''email'').touched">
   Email is required
  </div>
  <label>
   <span>Confirm address</span>
   <input type="email" placeholder="Confirm your email address" 
    formControlName="confirm">
  </label>
  <div*ngIf="user.get(''account'').get(''confirm'').hasError(''required'') && 
    user.get(''account'').get(''confirm'').touched">
   Confirming email is required
  </div>
  </div>
  <button type="submit" [disabled]="user.invalid">Sign up</button>
 </form>
 `
})
export class SignupFormComponent implements OnInit {
 user: FormGroup;
 constructor() {}
 ngOnInit() {
 this.user = new FormGroup({
  name: new FormControl('''', [Validators.required, Validators.minLength(2)]),
  account: new FormGroup({
  email: new FormControl('''', Validators.required),
  confirm: new FormControl('''', Validators.required)
  })
 });
 }
 onSubmit({ value, valid }: { value: User, valid: boolean }) {
 console.log(value, valid);
 }
}

功能是实现了,但创建 FormGroup 对象的方式有点繁琐,Angular 团队也意识到这点,因此为我们提供 FormBuilder ,来简化上面的操作。

Simplifying with FormBuilder

首先我们需要从 @angular/forms 中导入 FormBuilder:

import { FormBuilder, FormGroup, Validators } from ''@angular/forms'';

export class SignupFormComponent implements OnInit {
 user: FormGroup;
 constructor(private fb: FormBuilder) {}
 ...
}

然后我们使用 FormBuilder 对象提供的 group() 方法,来创建 FormGroup 和 FormControl 对象:

调整前的代码 (未使用FormBuilder):

ngOnInit() {
 this.user = new FormGroup({
 name: new FormControl('''', [Validators.required, Validators.minLength(2)]),
 account: new FormGroup({
  email: new FormControl('''', Validators.required),
  confirm: new FormControl('''', Validators.required)
 })
 });
}

调整后的代码 (使用FormBuilder):

ngOnInit() {
 this.user = this.fb.group({
 name: ['''', [Validators.required, Validators.minLength(2)]],
 account: this.fb.group({
  email: ['''', Validators.required],
  confirm: ['''', Validators.required]
 })
 });
}

对比一下调整前和调整后的代码,是不是感觉一下子方便了许多。此时更新完后完整的代码如下:

@Component({...})
export class SignupFormComponent implements OnInit {
 user: FormGroup;
 constructor(private fb: FormBuilder) {}
 ngOnInit() {
 this.user = this.fb.group({
  name: ['''', [Validators.required, Validators.minLength(2)]],
  account: this.fb.group({
  email: ['''', Validators.required],
  confirm: ['''', Validators.required]
  })
 });
 }
 onSubmit({ value, valid }: { value: User, valid: boolean }) {
 console.log(value, valid);
 }
}

我有话说

Template-Driven Forms vs Reactive Forms

Template-Driven Forms (模板驱动表单) 的特点

  • 使用方便
  • 适用于简单的场景
  • 通过 [(ngModel)] 实现数据双向绑定
  • 最小化组件类的代码
  • 不易于单元测试

Reactive Forms (响应式表单) 的特点

  • 比较灵活
  • 适用于复杂的场景
  • 简化了HTML模板的代码,把验证逻辑抽离到组件类中
  • 方便的跟踪表单控件值的变化
  • 易于单元测试

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对的支持。

您可能感兴趣的文章:
  • React中使用async validator进行表单验证的实例代码
  • ReactJS实现表单的单选多选和反选的示例
  • 详解Angular Reactive Form 表单验证
  • 基于React实现表单数据的添加和删除详解
  • 通过实例学习React中事件节流防抖
  • React中阻止事件冒泡的问题详析
  • 详解react关于事件绑定this的四种方式
  • React.Js添加与删除onScroll事件的方法详解
  • 在React中如何优雅的处理事件响应详解
  • 详解Python的Twisted框架中reactor事件管理器的用法
  • 详细分析React 表单与事件

关于angular reactive form的介绍现已完结,谢谢您的耐心阅读,如果想了解更多关于Angular 2:ReactiveForm更新模型、Angular 4 Reactive Forms切换隐藏表单元素的验证、Angular 4.x Reactive Forms、Angular 4.x中表单Reactive Forms详解的相关知识,请在本站寻找。

本文标签: