将FormControl传递给子组件-没有指定名称的表单控件的值访问器



我有一个输入组件customInput,它创建了一个经典的输入字段,并为它添加了一些布局趣味,没有额外的逻辑。

我想给它传递一个formControl,将它绑定到它包含的输入。

应该这样使用:

<form [formGroup]="form">
<custom-input [formControl]="form.controls['control']"></custom-input>
</form>

内部自定义输入:

export class HidInputFieldComponent  {
@Input() formControl: AbstractControl
...
}
<div class="container">
<input [formControl]="formControl"/>
<label>label</label>
</div>

现在,当我初始化组件时,我得到

没有指定名称的表单控件的值访问器

在我的组件构造函数中记录控件,它是未定义的。

我做错了吗?还是没有办法绕过ControlValueAccessor?由于我实际上并没有构建自定义控件(我仍然使用经典输入(,这似乎是极端的

您不需要导入ControlValueAccessor或任何类似的东西就可以实现这一点。

您所需要做的就是将FormControl对象传递给您的子组件,如下所示:

<form [formGroup]="form">
<custom-input [control]="form.controls['theControlName']">
</custom-input>
</form>

这意味着你的自定义输入组件应该是这样的:

import {Component, Input} from '@angular/core';
import {FormControl} from '@angular/forms';
@Component({
selector: 'custom-input',
templateUrl: './input.component.html',
styleUrls: ['./input.component.scss']
})
export class InputComponent {
@Input() control: FormControl;
}

模板:

<input [formControl]="control">

就是这样。

如果您实现这样的自定义输入,则不必将父窗体组带入子组件逻辑,这在那里是完全没有必要的(除非您需要对它或窗体控件的其他部分进行一些操作(。

此外,将FormControl对象传递给自定义输入将使您可以访问它的属性,而无需引用FormGroup,然后获取特定的控件,因为这是在父组件上完成的工作。

我希望这个解决方案有助于简化许多人的工作,因为制作这种自定义控件是很常见的。

Johann Garrido的这个答案很好,但它引入了额外的输入[control],在使用自定义组件时需要始终记住。

Plus,它绑定到直接工作到ReactiveFormsModule,因为它只接受FormControl实例。

在我看来,更好的方法是实现ControlValueAccessor接口,但要利用一些变通方法来避免重复控制处理:

export const NOOP_VALUE_ACCESSOR: ControlValueAccessor = {
writeValue(): void {},
registerOnChange(): void {},
registerOnTouched(): void {}
};

并在包装表单控件的组件中使用NOOP_VALUE_ACCESSOR

@Component({
selector: "wrapped-input",
template: `
<mat-form-field class="example-full-width">
<mat-label>Wrapped input</mat-label>
<!--We pass NgControl to regular MatInput -->
<input matInput [formControl]="ngControl.control" />
</mat-form-field>
`
})
export class WrappedInput {
constructor(@Self() @Optional() public ngControl: NgControl) {
if (this.ngControl) {
// Note: we provide the value accessor through here, instead of
// the `providers` to avoid running into a circular import.
// And we use NOOP_VALUE_ACCESSOR so WrappedInput don't do anything with NgControl
this.ngControl.valueAccessor = NOOP_VALUE_ACCESSOR;
}
}
}

这样Angular就会认为WrappedInput的工作方式与实现ControlValueAccessor接口的任何其他组件(例如MatInput(类似。

另外,它将同时适用于ReactiveFormsModule和常规FormsModule

WrappedInput可以这样使用:

<wrapped-input [formControl]="sourceControl"></wrapped-input>

以下是您可以玩的完整工作堆叠:https://stackblitz.com/edit/angular-wrapped-form-control-example?file=src/app/app.ts

使用FormGroupDirective

此指令接受现有的FormGroup实例。那就好了使用此FormGroup实例匹配任何子FormControl、FormGroup、,和FormArray实例到子FormControlName、FormGroupName和FormArrayName指令。

这样做,您可以从父访问子窗体控件

@Component({
selector: 'app-custom-input',
templateUrl: './custom-input.html',
viewProviders:[{ provide: ControlContainer, useExisting: FormGroupDirective}]
})    
export class HidInputFieldComponent  {
constructor(private fcd:FormGroupDirective) {       
}
ngOnInit() {
this.fcd.form.addControl('formControl',new FormControl(''));
}
}
<div class="container">
<input [formControl]="formControl"/>
<label>label</label>
</div>

我知道我参加聚会迟到了,但我偶然发现了这个问题,也有同样的问题,所以我希望这能对未来的人有所帮助。

在我的案例中,将@Input() formControl: FormControl重命名为@Input() control: FormControl修复了错误。

在TS文件中为自己创建一个自定义的getCtrl函数:

getCtrl(name: string): FormControl {
const ctrl = this.form.get(name) as FormControl;
if (!ctrl) throw 'Missing Form Control for ' + name;
return ctrl;
}

然后在模板中多次调用它:

<div>
<app-form-control
[control]="getCtrl('language')"
></app-form-control>
</div>

您也可以尝试实现ControlValueAccessor接口类。

@Component({
selector: 'app-custom-input',
templateUrl: './app-custom-input.component.html'
providers: [
{
provide: NG_VALUE_ACCESSOR,
multi: true,
useExisting: forwardRef(() => HidInputFieldComponent)
}
]
})
export class HidInputFieldComponent implements OnInit, ControlValueAccessor {
@Output() setDropdownEvent: EventEmitter<any> = new EventEmitter();
ngOnInit() {}
writeValue(value: any) {}
propagateChange(time: any) {
console.log(time);
}
registerOnChange(fn) {
this.propagateChange = fn;
}
registerOnTouched() {}
}

最新更新