如何将NgControl状态传递给Angular中的子组件,实现ControlValueAccessor?
提供如何将NgControl状态传递给Angular中的子组件,实现ControlValueAccessor?,angular,Angular,提供 { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => TestingComponent), multi: true } 注入式NGC控制 constructor(@Self() @Optional() public control: NgControl) { this.control && (this.control.valueAccessor = this); } 但这里缺少了什么
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => TestingComponent),
multi: true
}
注入式NGC控制
constructor(@Self() @Optional() public control: NgControl) {
this.control && (this.control.valueAccessor = this);
}
但这里缺少了什么
尽管@Eliseo的答案很有解释性,但还有一个补充。。。如果您想同时使用外部验证器和内部验证器,那么必须相应地设置父NgControl验证器。此外,如果您想使用验证,您需要使用ngDoCheck lifecycle hook来处理Ngdocontrol touch状态,因为下面的我是一个最终工作解决方案
@Component({
selector: 'app-testing',
templateUrl: 'testing.component.html'
})
export class TestingComponent implements ControlValueAccessor, DoCheck, AfterViewInit {
@Input()
public required: boolean;
@ViewChild('input', { read: NgControl })
private inputNgModel: NgModel;
public value: number;
public ngControlTouched: boolean;
constructor(@Optional() @Self() public ngControl: NgControl) {
if (this.ngControl != null) this.ngControl.valueAccessor = this;
}
ngDoCheck() {
if (this.ngControlTouched !== this.ngControl.touched) {
this.ngControlTouched = this.ngControl.touched;
}
}
ngAfterViewInit() {
// Setting ngModel validators to parent NgControl
this.ngControl.control.setValidators(this.inputNgModel.validator);
}
/**
* ControlValueAccessor Implementation
* Methods below
*/
writeValue(value: number): void {
this.value = value;
this.onChange(this.value);
}
onChange: (_: any) => void = (_: any) => {};
onTouched: () => void = () => {};
registerOnChange(fn: any): void {
this.onChange = fn;
}
registerOnTouched(fn: any): void {
this.onTouched = fn;
}
}
// Template
<input
#input="ngModel"
type="text"
class="form-control"
[class.is-invalid]="input.invalid && (input.dirty || input.touched || ngControlTouched)"
[(ngModel)]="value"
(ngModelChange)="onChange(value)"
(blur)="onTouched()"
[required]="required"
/>
// Usage
<app-testing [(ngModel)]="propertyDetails.whatThreeWords" name="testing" required></app-testing>
@组件({
选择器:“应用程序测试”,
templateUrl:'testing.component.html'
})
导出类TestingComponent实现ControlValueAccessor、DoCheck、AfterViewInit{
@输入()
公共要求:布尔值;
@ViewChild('input',{read:NgControl})
私有输入模型:NgModel;
公共价值:数字;
公共属性:布尔;
构造函数(@Optional()@Self()public ngControl:ngControl){
如果(this.ngControl!=null)this.ngControl.valueAccessor=this;
}
ngDoCheck(){
if(this.ngcontroltoucted!==this.ngControl.toucted){
this.ngcontroltouch=this.ngControl.touch;
}
}
ngAfterViewInit(){
//将ngModel验证程序设置为父NgControl
this.ngControl.control.setValidators(this.inputNgModel.validator);
}
/**
*ControlValueAccessor实现
*方法如下
*/
writeValue(值:number):无效{
这个值=值;
this.onChange(this.value);
}
onChange:((任意)=>void=((任意)=>{};
onTouched:()=>void=()=>{};
注册变更(fn:任何):无效{
this.onChange=fn;
}
注册人(fn:任何):无效{
this.ontoched=fn;
}
}
//模板
//用法
您有两个选择:
注入NgControl,为此,您需要删除提供程序并使构造函数
constructor(public control:NgControl){
if (this.control != null) {
this.control.valueAccessor = this;
}
}
然后你可以像这样装饰你的输入
<input [ngClass]="{'ng-invalid':control.invalid,'ng-valid':control.valid...}">
并创建一个函数“copyClass”
您可以在writeValue、Change和OnTouched中调用此函数
我能想象的最简单的例子是
注意:如果您的问题是在组件中使用材料角度,则该技术使用的是customErrorMatcher,如果您想在
更新另一个方法是为输入设置相同的验证器。为此,我们使用viewChild获取输入,在ngAfterViewInit中,它等于验证器
@ViewChild('input',{static:false,read:NgControl}) input
ngAfterViewInit()
{
if (this.control != null) {
this.input.control.setValidators(this.control.control.validator)
}
}
看
在最后更新时如果我们想在控件中有一个自定义错误,我们可以使用函数validate来获取控件,而不是注入构造函数。组件变得像
@Component({
...
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => CustomFormControlComponent),
multi: true
},
{
provide: NG_VALIDATORS,
useExisting: forwardRef(() => CustomFormControlComponent),
multi: true,
}]
})
export class CustomFormControlComponent implements ControlValueAccessor,
Validator, AfterViewInit {
...
control:any
@ViewChild('input', { static: false, read: NgControl }) input
constructor() {
}
ngAfterViewInit() {
this.validate(null)
}
validate(control: AbstractControl): ValidationErrors | null{
if (!this.control)
this.control=control;
if (this.control && this.input)
this.input.control.setValidators(this.control.validator)
if (control.value=="qqq")
return {error:"Inner error:The value is 1"}
return null
}
新的问题出在哪里?必须通过父组件NgForm验证子组件(form.controls[key].markAsTouched();)。但是验证是在一个子系统上进行的。InActingControl不允许我将以下内容应用于子级的输入[class.is invalid]=“control.invalid&(control.dirty | | control.touch)”您可以共享自定义控件类的代码吗?我今天早上刚刚查看了我的代码,注意到我还没有实现模糊事件。。。然而,我目前正在寻找一种方法,在子组件上实现自定义验证器(必需、模式和自定义必须存在),并将验证状态和验证状态转移到父窗体验证器可以在组件外部-那么您不需要在组件内部或任何东西,请参见“是”,但在我的例子中,我需要它们在里面,只有required可以通过输入装饰器有条件地设置。提供了NG_验证程序,安装验证,但不影响子控件验证状态。。。父输入为ng无效,而子输入为ng validmake内部的验证器没有多大意义-仅当它是自定义验证器时。无论如何,我刚刚发现了一种更简单的方法,让我们的“输入”得到错误,那就是复制验证器-查看我的更新答案-如果我们内部有一个自定义验证器,我们需要使用setvalidator,如
setValidators([…this.control.control.validator,newValidator])
谢谢@Eliseo。我花了很多钱来控制触摸状态。请看我的更新。你的回答真的帮助了我!
copyClass()
{
setTimeout(()=>{
this.class=this.elementRef.nativeElement.getAttribute('class')
})
}
@ViewChild('input',{static:false,read:NgControl}) input
ngAfterViewInit()
{
if (this.control != null) {
this.input.control.setValidators(this.control.control.validator)
}
}
@Component({
...
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => CustomFormControlComponent),
multi: true
},
{
provide: NG_VALIDATORS,
useExisting: forwardRef(() => CustomFormControlComponent),
multi: true,
}]
})
export class CustomFormControlComponent implements ControlValueAccessor,
Validator, AfterViewInit {
...
control:any
@ViewChild('input', { static: false, read: NgControl }) input
constructor() {
}
ngAfterViewInit() {
this.validate(null)
}
validate(control: AbstractControl): ValidationErrors | null{
if (!this.control)
this.control=control;
if (this.control && this.input)
this.input.control.setValidators(this.control.validator)
if (control.value=="qqq")
return {error:"Inner error:The value is 1"}
return null
}