如何在Angular的自定义控件组件中触摸内部控件?

Posted

技术标签:

【中文标题】如何在Angular的自定义控件组件中触摸内部控件?【英文标题】:How to touch inner control in custom control component in Angular? 【发布时间】:2021-02-22 18:39:12 【问题描述】:

我有一个带有我自己的自定义控件组件的表单:

@Component(
  selector: "my-app",
  template: `
    <form [formGroup]="form">
      <app-custom-control formControlName="customControl"></app-custom-control>
    </form>

    <button (click)="touch()">
      Touch!
    </button>
  `,
  styleUrls: ["./app.component.css"]
)
export class AppComponent 
  form: FormGroup;

  constructor(private fb: FormBuilder) 

  ngOnInit() 
    this.form = this.fb.group(
      customControl: "1"
    );
  

  touch() 
    this.form.markAllAsTouched();
  

我的自定义控件组件内部有另一个自定义控件组件(在我的真实应用中需要它,因为外部控件组件有两种模式 - 读取和编辑):

@Component(
  selector: "app-custom-control",
  template: `
    <ng-select [ngModel]="value" [items]="items"></ng-select>
  `,
  styleUrls: ["./custom-control.component.css"],
  providers: [
    
      provide: NG_VALUE_ACCESSOR,
      useExisting: CustomControlComponent,
      multi: true
    
  ]
)
export class CustomControlComponent implements ControlValueAccessor, OnInit 
  items = ["1", "2"];

  value = null;
  onChange = (value: any) => ;
  onTouched = () => ;

  constructor() 

  registerOnChange(fn: any) 
    this.onChange = fn;
  

  registerOnTouched(fn: () => ): void 
    this.onTouched = fn;
  

  writeValue(outsideValue: number) 
    this.value = outsideValue;
  

可以将NG_VALIDATORS 添加到CustomControlComponent 并实现组件的验证方式(它只会提升ng-select 错误)。但是在包装表单的组件中执行this.form.markAllAsTouched() 时,我真的不知道如何触摸内部组件(ng-select)。

我尝试在 ngDoCheck 生命周期方法中进行操作,但它似乎运行了太多次,这是不可接受的解决方案。

游乐场:https://stackblitz.com/edit/angular-ivy-u3kdfj

【问题讨论】:

【参考方案1】:

在 github 中有几个相关的问题:

https://github.com/angular/angular/issues/10887 https://github.com/angular/angular/issues/17736

您可以做的是为 ControlValueAccessor 控件(outerControl)和内部 NgModel 控件(innerControl)获取 NgControl:

custom-control.component.ts

export class CustomControlComponent implements ControlValueAccessor, AfterViewInit 
  @ViewChild(NgControl) innerNgControl: NgControl;

  constructor(private inj: Injector) 

  ...

  ngAfterViewInit()  
    const outerControl = this.inj.get(NgControl).control;
    ...
  

然后你可以使用这个技巧绕过触摸状态到内部控制:

const prevMarkAsTouched = outerControl.markAsTouched;
outerControl.markAsTouched = (...args: any) =>  
  this.innerNgControl.control.markAsTouched();
  prevMarkAsTouched.bind(outerControl)(...args);
;

Forked Stackblitz

【讨论】:

以上是关于如何在Angular的自定义控件组件中触摸内部控件?的主要内容,如果未能解决你的问题,请参考以下文章

Angular 6. 如何在创建的自定义控件中赋予验证状态这个控件?

如何在自定义控件中添加所需的星号?

Angular 2 - 单元测试绑定到嵌套的自定义表单控件

如何在黑莓10中处理自定义控件的触摸事件

Angular 2 自定义表单输入

Angular2:例外:在控件上找不到指令注释