Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/angular/30.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何将NgControl状态传递给Angular中的子组件,实现ControlValueAccessor?_Angular - Fatal编程技术网

如何将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
  }