jbt*_*btd 124 javascript typescript angular angular4-forms
我对自己的表格有点疑惑.
我做了一个自定义元素:
<div formControlName="surveyType">
<div *ngFor="let type of surveyTypes"
(click)="onSelectType(type)"
[class.selected]="type === selectedType">
<md-icon>{{ type.icon }}</md-icon>
<span>{{ type.description }}</span>
</div>
</div>
Run Code Online (Sandbox Code Playgroud)
我尝试添加formControlName但有角度不想知道任何事情,只是说:
<div formControlName="surveyType">
<div *ngFor="let type of surveyTypes"
(click)="onSelectType(type)"
[class.selected]="type === selectedType">
<md-icon>{{ type.icon }}</md-icon>
<span>{{ type.description }}</span>
</div>
</div>
Run Code Online (Sandbox Code Playgroud)
我试图添加ngDefaultControl但没有成功.这似乎是因为没有输入/选择...但我不知道该怎么做.
我想将我的点击绑定到这个formControl,以便当有人点击整个卡片时将我的'type'推入formControl.可能吗?
Laz*_*vić 215
您只能使用formControlName实现的指令ControlValueAccessor.
因此,为了做你想做的事,你必须创建一个实现的组件ControlValueAccessor,这意味着实现以下三个功能:
writeValue (告诉Angular如何将模型中的值写入视图)registerOnChange (注册视图更改时调用的处理函数)registerOnTouched (注册在组件接收触摸事件时要调用的处理程序,对于了解组件是否已被聚焦有用).然后,你必须告诉Angular这个指令是一个ControlValueAccessor(接口不会削减它,因为当TypeScript被编译为JavaScript时它被从代码中剥离).您可以通过注册提供商来完成此操作.
提供者应提供NG_VALUE_ACCESSOR并使用现有值.你还需要一个forwardRef.请注意,NG_VALUE_ACCESSOR应该是多提供商.
例如,如果您的自定义指令名为MyControlComponent,则应在传递给@Component装饰器的对象内的以下行中添加一些内容:
providers: [
{
provide: NG_VALUE_ACCESSOR,
multi: true,
useExisting: forwardRef(() => MyControlComponent),
}
]
Run Code Online (Sandbox Code Playgroud)
您的组件已准备好使用.使用模板驱动的表单,ngModel绑定现在可以正常工作.
使用反应式表单,您现在可以正常使用formControlName,表单控件将按预期运行.
Veg*_*ega 55
我认为你应该使用formControlName="surveyType"a input而不是adiv
ber*_*ing 23
该错误意味着,当您将 aformControl放在div. 要解决此问题,您有两个选择。
formControlName放在一个元素上,该元素由 Angular 开箱即用。它们是:input、textarea和select。ControlValueAccessor接口。通过这样做,您是在告诉 Angular “如何访问您的控件的值”(因此得名)。或者简单地说:该怎么做,当您将 aformControlName放在一个元素上时,它自然没有与之关联的值。现在,ControlValueAccessor最初实现接口可能有点令人生畏。特别是因为没有太多关于此的好的文档,并且您需要在代码中添加大量样板。因此,让我尝试通过一些简单易懂的步骤将其分解。
为了实现ControlValueAccessor,您需要创建一个新组件(或指令)。将与表单控件相关的代码移到那里。像这样,它也很容易重复使用。组件内部已经有一个控件可能是您需要实现ControlValueAccessor接口的首要原因,否则您将无法将自定义组件与 Angular 表单一起使用。
实现ControlValueAccessor接口非常冗长,这是它附带的样板:
import {Component, OnInit, forwardRef} from '@angular/core';
import {ControlValueAccessor, FormControl, NG_VALUE_ACCESSOR} from '@angular/forms';
@Component({
selector: 'app-custom-input',
templateUrl: './custom-input.component.html',
styleUrls: ['./custom-input.component.scss'],
// a) copy paste this providers property (adjust the component name in the forward ref)
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => CustomInputComponent),
multi: true
}
]
})
// b) Add "implements ControlValueAccessor"
export class CustomInputComponent implements ControlValueAccessor {
// c) copy paste this code
onChange: any = () => {}
onTouch: any = () => {}
registerOnChange(fn: any): void {
this.onChange = fn;
}
registerOnTouched(fn: any): void {
this.onTouch = fn;
}
// d) copy paste this code
writeValue(input: string) {
// TODO
}
Run Code Online (Sandbox Code Playgroud)
那么各个部分在做什么呢?
ControlValueAccessor接口ControlValueAccessor接口onChange,onTouch并在运行时使用它自己的实现,这样你就可以调用这些函数。所以理解这一点很重要:您不需要自己实现 onChange 和 onTouch(除了最初的空实现)。您对 (c) 所做的唯一一件事就是让 Angular 将它自己的函数附加到您的类中。为什么?这样你就可以在适当的时候调用Angular 提供的onChange和onTouch方法。我们将在下面看到它是如何工作的。writeValue当我们实现它时,我们还将在下一节中看到该方法是如何工作的。我已经把它放在这里,所以所有必需的属性ControlValueAccessor都被实现,你的代码仍然可以编译。什么writeValue是在自定义组件内部做一些事情,当表单控件在外部更改时。例如,如果您命名了自定义表单控件组件,app-custom-input并且将在父组件中使用它,如下所示:
<form [formGroup]="form">
<app-custom-input formControlName="myFormControl"></app-custom-input>
</form>
Run Code Online (Sandbox Code Playgroud)
然后writeValue每当父组件以某种方式更改 的值时触发myFormControl。例如,这可能是在表单 ( this.form = this.formBuilder.group({myFormControl: ""});)的初始化期间或在表单重置时this.form.reset();。
如果表单控件的值在外部发生变化,您通常想要做的是将其写入代表表单控件值的局部变量。例如,如果您CustomInputComponent围绕基于文本的表单控件,它可能如下所示:
writeValue(input: string) {
this.input = input;
}
Run Code Online (Sandbox Code Playgroud)
并在 html 中CustomInputComponent:
<input type="text"
[ngModel]="input">
Run Code Online (Sandbox Code Playgroud)
您也可以按照 Angular 文档中的描述将其直接写入输入元素。
现在您已经处理了当外部发生变化时组件内部发生的情况。现在让我们看看另一个方向。当组件内部发生变化时,您如何通知外部世界?
下一步是通知父组件有关CustomInputComponent. 这就是上面 (c)中的onChange和onTouch函数发挥作用的地方。通过调用这些函数,您可以将组件内部的更改通知给外部。为了将值的更改传播到外部,您需要使用新值作为参数调用 onChange。例如,如果用户input在自定义组件的字段中键入某些内容,则onChange使用更新后的值调用:
<input type="text"
[ngModel]="input"
(ngModelChange)="onChange($event)">
Run Code Online (Sandbox Code Playgroud)
如果你再次检查上面的实现 (c),你会看到发生了什么:Angular 将它自己的实现绑定到onChange类属性。该实现需要一个参数,即更新的控制值。您现在所做的是调用该方法,从而让 Angular 了解更改。Angular 现在将继续更改外部的表单值。这是这一切的关键部分。您告诉 Angular 何时应该更新表单控件以及调用onChange. 您已经为其提供了“访问控制值”的方法。
顺便说一句:名字onChange是我自己选的。例如,您可以在此处选择任何内容propagateChange或类似内容。不管你怎么命名它,它都是带有一个参数的相同函数,它由 Angular 提供,并且registerOnChange在运行时通过方法绑定到你的类。
由于表单控件可以被“触摸”,您还应该让 Angular 了解何时您的自定义表单控件被触摸。你可以做到,你猜对了,通过调用onTouch函数。因此,对于我们这里的示例,如果您想与 Angular 对开箱即用的表单控件的处理方式保持一致,您应该onTouch在输入字段模糊时调用:
<input type="text"
[(ngModel)]="input"
(ngModelChange)="onChange($event)"
(blur)="onTouch()">
Run Code Online (Sandbox Code Playgroud)
同样,onTouch是我选择的名称,但它的实际功能是由 Angular 提供的,它采用零参数。这是有道理的,因为您只是让 Angular 知道表单控件已被触及。
那么当它们组合在一起时,它看起来如何呢?它应该是这样的:
// custom-input.component.ts
import {Component, OnInit, forwardRef} from '@angular/core';
import {ControlValueAccessor, FormControl, NG_VALUE_ACCESSOR} from '@angular/forms';
@Component({
selector: 'app-custom-input',
templateUrl: './custom-input.component.html',
styleUrls: ['./custom-input.component.scss'],
// Step 1: copy paste this providers property
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => CustomInputComponent),
multi: true
}
]
})
// Step 2: Add "implements ControlValueAccessor"
export class CustomInputComponent implements ControlValueAccessor {
// Step 3: Copy paste this stuff here
onChange: any = () => {}
onTouch: any = () => {}
registerOnChange(fn: any): void {
this.onChange = fn;
}
registerOnTouched(fn: any): void {
this.onTouch = fn;
}
// Step 4: Define what should happen in this component, if something changes outside
input: string;
writeValue(input: string) {
this.input = input;
}
// Step 5: Handle what should happen on the outside, if something changes on the inside
// in this simple case, we've handled all of that in the .html
// a) we've bound to the local variable with ngModel
// b) we emit to the ouside by calling onChange on ngModelChange
}
Run Code Online (Sandbox Code Playgroud)
// custom-input.component.html
<input type="text"
[(ngModel)]="input"
(ngModelChange)="onChange($event)"
(blur)="onTouch()">
Run Code Online (Sandbox Code Playgroud)
// parent.component.html
<app-custom-input [formControl]="inputTwo"></app-custom-input>
// OR
<form [formGroup]="form" >
<app-custom-input formControlName="myFormControl"></app-custom-input>
</form>
Run Code Online (Sandbox Code Playgroud)
请注意,控制值访问器不是嵌套表单组的正确工具。对于嵌套表单组,您可以简单地使用 an@Input() subform代替。控制值访问器旨在包装controls,而不是groups! 请参阅此示例如何使用嵌套表单的输入:https : //stackblitz.com/edit/angular-nested-forms-input-2
| 归档时间: |
|
| 查看次数: |
139183 次 |
| 最近记录: |