15

有一个自定义输入组件,它以带有验证的反应形式使用:

@Component({
    moduleId: module.id.toString(),
    selector: 'custom-select',
    templateUrl: 'custom-select.component.html',
    styleUrls: ['custom-select.component.css']
})
export class CustomSelectComponent {
    @Input() public items: SelectModel[];
    public model: SelectModel;
    constructor(private customSelectService: CustomSelectService) {
        this.customSelectService.Selected.subscribe((data: SelectModel) => {
            this.model = data;
        });
    }
    public newSelect(select: SelectModel): void {
        this.customSelectService.updateSelected(select);
    }
}

效果很好,我custom-select以反应形式使用并希望像下面这样验证它:

<custom-select id="country" [items]="selectItems" formControlName="country"></custom-select>
<div *ngIf=" myFrom.controls['country'].invalid && (myFrom.controls['country'].dirty 
             || myFrom.controls['country'].touched) " class="ha-control-alert">
    <div *ngIf="myFrom.controls['country'].hasError('required')">Country is required</div>
</div>

这就是我在组件中声明表单的方式

this.myFrom = this.formBuilder.group({
    country: [null, Validators.required],
})

但是当我添加formControlName验证时,它会出现错误,显示No value accessor for form control with name: 'country'。我该如何处理?

4

2 回答 2

3

脚步

  1. 在装饰器中添加 NG_VALUE_ACCESSOR 的提供者
  2. 在类中实现 ControlValueAccessor
  3. 像这样创建 onChange 函数 onChange = (value: boolean) => {};
  4. 添加ControlValueAccessor接口的registerOnChange、writeValue和registerOnTouched方法
  5. 在将要更改 select 值的方法中,调用作为参数传递 select 值的 onChange 函数。
        import ...
        import { Component, forwardRef } from '@angular/core';
        import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
        
    
        @Component({
            moduleId: module.id.toString(),
            selector: 'custom-select',
            templateUrl: 'custom-select.component.html',
            styleUrls: ['custom-select.component.css'],
            // STEP 1
            providers: [{
              provide: NG_VALUE_ACCESSOR,
              multi: true,
              useExisting: forwardRef(() => CustomSelectComponent)
            }]
        })
        // STEP 2
        export class CustomSelectComponent implements ControlValueAccessor {
            // STEP 3
            onChange = (value: SelectModel) => {};
            @Input() public items: SelectModel[];
            public model: SelectModel;
            constructor(private customSelectService: CustomSelectService) {
                this.customSelectService.Selected.subscribe((data: SelectModel) => {
                    this.model = data;
                });
            }
            public newSelect(select: SelectModel): void {
                // STEP 5
                this.onChange(select);
                this.customSelectService.updateSelected(select);
            }
            // STEP 4
            registerOnChange(fn: (value: SelectModel) => void): void {
                this.onChange = fn;
            }
            writeValue() {}
            registerOnTouched(){}
        }

不要忘记在选择器中添加 formControlName。

于 2021-04-10T20:40:17.473 回答
1

将此添加到孩子的提供者:

viewProviders: [{
  provide: ControlContainer, 
  useExisting: FormGroupDirective 
}]

它首先失败的原因是它没有在 FormGroup 的父范围内查找,所以这只是将它传递给子级。

然后你需要让它认为你的控件有一个 valueAccessor - 它不需要它,因为你刚刚创建了一种“垫片”。

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

然后在您孩子的构造函数中:

constructor(@Self() @Optional() public ngControl: NgControl) 
{
    if (this.ngControl)
    {
        this.ngControl.valueAccessor = NOOP_VALUE_ACCESSOR;
    }
}

然后,您可以在孩子内部正常使用 formControlName。

如果您需要从父组件传递 formControlName(如果它是可重用控件,您可能会这样做)只需创建一个字符串@Input属性并在子组件中执行此操作:

    <input matInput [placeholder]="desc" 
           [formControlName]="formControlName">
于 2021-07-12T20:36:21.703 回答