【问题标题】:Form with Custom Component does not recognize control带有自定义组件的表单无法识别控件
【发布时间】:2019-09-15 15:17:44
【问题描述】:

我正在使用具有以下代码的自定义组件(自定义文本)

    @Component({
      selector: 'custom-text, [custom-text]',
      templateUrl: './custom-text.template.html',
      styleUrls: ['./custom-text.component.scss']
    })
    export class CustomTextComponent implements OnInit {


      constructor() { }

      ngOnInit() {
      }
    }

Inside the custom-text.template.html

    <mat-form-field>
      <input matInput 
            id="controlid" 
            name="controlname" 
            maxlength="8"
            [(ngModel)]="value">
    </mat-form-field>

当我将此控件包含在另一个组件的表单(模板驱动)中时。

    <form #someForm="ngForm">
        <div custom-text></div>
    </form>

or 

<form #someForm="ngForm">
    <custom-text></custom-text>
</form>

我无法使用 someForm.controls['controlId'] 获取控件实例

我做错了什么。

【问题讨论】:

  • 来自控件的自定义必须实现 ControlValueAccessor,参见例如stackoverflow.com/questions/40009149/…
  • @Eliseo 感谢您帮助我解决这个问题,但是,当我将它与 mat-form-field 一起使用时,它无法按预期工作,父表单不会将此控件视为它。
  • 我在下面介绍了如何根据输入垫制作自定义控件验证器
  • 我添加了另一个不使用自定义表单控件的响应。在 stackblitz 中,您有两种选择

标签: angular angular7 angular-forms angular-template-form


【解决方案1】:

我在stackblitz 中留下了基于材料输入的最简单的自定义表单控件。

如您所见,实现了 ControlValueAccessor,它具有以下功能:

onChange:any; //declare this function to indicate in any time that you change the value
onTouched:any; //declare this function to indicate in any time that your compoment is touched

 writeValue(value: any[]|any): void {
    this.value=value;
  }

  registerOnChange(fn: any): void {
    this.onChange = fn;
  }

  registerOnTouched(fn: any): void {
    this.onTouched = fn;
  }

  setDisabledState(isDisabled: boolean): void {
    this.disabled=isDisabled
  }

还有像这样的提供者

   {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => CustomMat),
      multi: true
    }

如果你想在组件内部进行验证,你需要添加一个新的提供者

   {
      provide: NG_VALIDATORS,
      useExisting: forwardRef(() => CustomMat),
      multi: true,
    }

并创建函数验证

validate(control: AbstractControl): ValidationErrors | null{
      //your logic here
      return null;
  }

我又用了这两个函数:

setValue(value: any){
    this.value=value;
    this.onChange(value);

  }
  focusOut()
  {
    this.onTouched()
  }

调用更改和触摸的函数

01.04.20更新好了,这个自定义素材输入的问题是错误在输入中看不到反映,所以我们要做一些修改

这个想法是添加一个 customError 匹配器

export class CustomFieldErrorMatcher implements ErrorStateMatcher {
  constructor(private customControl: AbstractControl,private errors:any) { }

  isErrorState(control: AbstractControl | null, form: FormGroupDirective | NgForm | null): boolean {
    return this.customControl && this.customControl.touched && this.customControl.invalid;
  }
}

如果控件无效,那就是我们的内部输入无效。那么,很难知道这个“控制”是什么。为此,我们在 ngAfterViewInit 中注入了 NgControl,这个 ngControl 将成为我们的控件,噗

ngAfterViewInit(): void {
    const ngControl: NgControl = this.injector.get(NgControl, null);
    if (ngControl) {
      setTimeout(() => {
        this.control = ngControl.control;
         this.matcher = new CustomFieldErrorMatcher(this.control,null);
      })
    }
  }

至少添加这个匹配器

  <input name="input" matInput [ngModel]="value"
    (ngModelChange)="setValue($event)" 
    [errorStateMatcher]="matcher" >

你可以在this stackblitz看到

【讨论】:

  • 注入ngControl 是我见过的最好的“粉扑”。有了这个,我的组件现在与模板驱动的表单和反应表单控件兼容。我唯一不明白的是为什么我们需要setTimeout()
  • 只是为了避免最初的错误:Error: ExpressionChangedAfterItHasBeenCheckedError
【解决方案2】:

真正考虑您的问题,您不需要制作自定义表单组件,只需将组件作为输入传递给表单控件。只需添加一个输入

@Input() control
//And in .html
<mat-form-field>
      <input matInput 
            id="controlid" 
            name="controlname" 
            maxlength="8"
            [formControl]="control">
</mat-form-field>

你像这样使用组件

<form #someForm="ngForm">
    <div custom-text [control]="someForm.controls['controlId']"></div>
</form>

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-10-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多