序言
让我们谈谈角度的反应形式,学习自定义控件如何创建,使用和验证它们。 本文假定您已经熟悉了Angular框架,但想深入了解其具体细节。 祝好,让我们开始吧。
反应式和模板驱动形式
请确保我们在同一页面上。 角形具有两种类型的形式:
模板驱动形式和
反应形式 。
模板驱动表单是基于
双向绑定的表单(hi,angularjs)。 我们在类中指定字段(例如,用户名),在输入标签的html中绑定[[value]] =“ username”,并且当输入值更改时,用户名值也会更改。 在2011年,那真是太神奇了! 好的,但是有细微差别。 这样,构建复杂的形状将非常困难。
反应性表单是用于构建简单和复杂表单的便捷工具。 他们告诉我们,“创建表单类的实例(
FormGroup ),将控件的实例(
FormControl )传递给它,然后将其交给html,剩下的我会做。” 好吧,让我们尝试:
class UsefullComponent { public control = new FormControl(''); public formG = new FormGroup({username: control}); }
<form [formGroup]="formG"> <input type="text" formControlName="username"> </form>
结果,我们得到了一个带有二十一点的反应式表格,而且……嗯,您知道了所有便利设施。 例如,
formG.valueChanges将为我们提供一个可观察的(流)表单更改。 您还可以添加新控件,删除现有控件,更改验证规则,获取表单值(
formG.value )等。 所有这些都与一个
formG实例
一起工作 。
为了避免每次都手动创建上述类的实例,
Angular开发人员为我们提供了一个方便的
FormBuilder ,借助该工具,可以将上面的示例重写为
class UsefullComponent { public formG: FormGroup; constructor(private fb: FormBuilder) { this.formG = fb.group({ name: ''
自定义控件
Angular告诉我们“兄弟,如果您没有足够的本机控件(输入,日期,数字和其他控件),或者如果您根本不喜欢它们的某些功能,那么这是一个简单但功能强大的工具,可用于创建您自己的控件。” 自定义控件甚至可以用于响应式,甚至可以用于模板驱动的表单,并且可以使用指令来实现(令人惊讶!)。 知道组件是带有模板的指令(所需)后,我们编写:
import { Component, Input } from '@angular/core'; @Component({ selector: 'counter-control', template: ` <button (click)="down()">Down</button> {{ value }} <button (click)="up()">Up</button> ` }) class CounterControlComponent { @Input() value = 0; up() { this.value++; } down() { this.value - ; } }
像任何指令一样,它必须在模块中声明(这样,文章就不会增加,我们将忽略这些细微差别)。
现在我们可以使用创建的组件:
import { Component } from '@angular/core'; @Component({ selector: 'parent-component', template: ` <counter-control></counter-control> ` }) class ParentComponent {}
当然,所有方法都可以,但是表格在哪里? 至少是模板驱动的。
不必担心,遇到
ControlValueAccessor之后一切都会发生。
控制值访问器
为了使角度机制与您的自定义控件进行交互,您需要此控件来实现特定的接口。 此接口称为
ControlValueAccessor 。 当我们的对象(在这种情况下,控件)实现一个接口,而其他对象(角度形式)通过该接口与我们的对象交互时,这是OOP中多态性的一个很好的例子。
多亏了
ControlValueAccessor ,我们有了使用控件的单一方法,顺便说一句,它不仅用于创建自定义控件。 引擎盖下的Angular也使用此接口。 为了什么 并且只是为了使本机控件的行为具有统一的外观。 例如,在输入中,值包含在value属性中,在复选框中,该值是通过选中的属性确定的。 因此,每种控件都有其自己的ControlValueAccessor:用于输入和文本区域的DefaultValueAccessor,用于复选框的CheckboxControlValueAccessor,用于单选按钮的
RadioControlValueAccessor等。
但是使用
ControlValueAccessor角度会做什么? 一切都非常简单,它将来自模型的值写入DOM(视图),还将更改控制事件引发给
FormGroup和其他指令。
现在我们已经了解了
ControlValueAccessor ,我们可以将其应用于控件。
让我们看一下它的界面:
interface ControlValueAccessor { writeValue(value: any): void registerOnChange(fn: any): void registerOnTouched(fn: any): void }
writeValue(值:任意) -设置源(
新的FormControl(“我是默认值”) )或新的值超过
control.setValue(“我的设置值”)时调用 。
registerOnChange(fn:any) -定义值更改时应调用的处理程序的方法(fn是一个回调,它将通知表单该控件中的值已更改)。
registerOnTouched(fn:any) -定义在
blur事件上调用的回调(控件带有
touched标记)
import { Component, Input } from '@angular/core'; import { ControlValueAccessor } from '@angular/forms'; @Component({ selector: 'counter-control', template: ` <button (click)="down()">Down</button> {{ value }} <button (click)="up()">Up</button> ` }) class CounterControlComponent implements ControlValueAccessor { @Input() value = 0; onChange(_: any) {} up() { this.value++; } down() { this.value - ; } writeValue(value: any) { this.value = value; } registerOnChange(fn) { this.onChange = fn; } registerOnTouched() {} }
为了使父窗体知道控件中的更改,我们需要为value的每个更改调用
onChange方法。 为了不在每个方法(向上和向下)中编写
onChange调用,我们通过getter和setter来实现value字段:
目前,
Angular不知道实现
ControlValueAccessor的组件应被视为自定义控件,让我们告诉他:
import { Component, Input, forwardRef } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; @Component({ … providers: [{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => CounterControlComponent), multi: true }] }) class CounterControlComponent implements ControlValueAccessor { … }
在代码的这一部分中,我们对角度说“我想注册一个新的自定义控件(
提供:NG_VALUE_ACCESSOR ),使用该组件的现有实例(
此时将初始化我们的组件)(
useExisting:forwardRef(()=> CounterControlComponent )”
值) 。
multi:true表示使用此类标记(
NG_VALUE_ACCESSOR )可能存在多个依赖项。
不只是创建
现在该zayuzat我们的自定义控件了。 不要忘记将
FormsModule / ReactiveFormsModule添加到使用此控件的模块的导入中。
在模板驱动的表单中使用
这里的一切都很简单,通过
ngModel使用双向绑定,当模型更改时,我们将获得视图更改,反之亦然:
import { Component } from '@angular/core'; @Component({ selector: 'parent-component', template: ` <counter-control [(ngModel)]="controlValue"></counter-control> ` }) class ParentComponent { controlValue = 10; }
在反应形式中使用
如本文开头所述,通过
FormBuilder创建反应形式的实例,以html
呈现并享受:
import { Component, OnInit } from '@angular/core'; import { FormBuilder } from '@angular/forms'; @Component({ selector: 'parent-component', template: ` <form [formGroup]="form"> <counter-control formControlName="counter"></counter-control> </form> ` }) class ParentComponent implements OnInit { form: FormGroup; constructor(private fb: FormBuilder) {} ngOnInit() { this.form = this.fb.group({ counter: 5 }); } }
现在,这是带有我们自定义控件的成熟的反应形式,而所有机制的工作方式都与本机控件相同(我是否提到了多态性?)。 为了不加载当前文章,我们将在下一篇文章中讨论验证和显示自定义控件错误。
材料:
Pascal Precht中有关自定义控件的
文章 。
码头的形式成角度。
有关rxjs的
一系列文章 。