简体   繁体   English

Angular:带有 ControlValueAccessor 的自定义输入

[英]Angular: custom input with ControlValueAccessor

I'm not sure how can I use a custom component if it's wrapper under another component.如果自定义组件是另一个组件下的包装器,我不确定如何使用它。

Like:喜欢:

ComponentA_withForm
|
--ComponentA1_withWrapperOfCustomInput
  |
  --ComponentA11_withCustomInput

if I have a structure like this:如果我有这样的结构:

ComponentA_withForm
|
--ComponentA11_withCustomInput

Everything's fine一切安好

But for my case (tons of async data) I need a wrapper... Is it possible somehow to do this?但是对于我的情况(大量异步数据),我需要一个包装器......是否有可能以某种方式做到这一点?

Here is my fiddle code:这是我的小提琴代码:

ComponentA:组件A:

import { Component } from '@angular/core';
import { FormBuilder } from '@angular/forms';

@Component({
  selector: 'my-app',
  template: `<form [formGroup]="form"><custom-input-wrapper formControlName="someInput"></custom-input-wrapper></form> <p>value is: {{formVal | json}}</p>`
})
export class AppComponent {
  form = this.fb.group({
    someInput: [],
  });

  get formVal() {
    return this.form.getRawValue();
  }

  constructor(private fb: FormBuilder) { }
}

ComponentA1:组件A1:

import { Component } from '@angular/core';

@Component({
  selector: 'custom-input-wrapper',
  template: '<custom-input></custom-input>',
})
export class CustomInputWrapperComponent {
  constructor() { }
}

ComponentA11:组件A11:

import { Component, forwardRef } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';

@Component({
  selector: 'custom-input',
  template: `Hey there! <button (click)="inc()">Value: {{ value }}</button>`,
  providers: [{
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(() => CustomInputComponent),
    multi: true,
  }],
})
export class CustomInputComponent implements ControlValueAccessor {

  private value = 0;

  writeValue(value: number): void {
    this.value = value;
  }

  registerOnChange(fn: (_: any) => void): void {
    this.onChangeFn = fn;
  }

  registerOnTouched(fn: any): void {
  }

  inc() {
    this.value = this.value + 1;
    this.onChangeFn(this.value);
  }

  onChangeFn = (_: any) => { };
}

And here I have a working sample: https://stackblitz.com/edit/angular-qmrj3a在这里我有一个工作示例: https : //stackblitz.com/edit/angular-qmrj3a

so: basically removing & refactoring code not to use CustomInputWrapperComponent makes my code working.所以:基本上删除和重构代码不使用CustomInputWrapperComponent使我的代码工作。 But I need this wrapper and I'm not sure how to pass formControlName then.但是我需要这个包装器,我不确定如何传递formControlName

I don't want a dirty solution with passing parent formGroup :)我不想要通过父 formGroup 的肮脏解决方案:)

Since you don't want a dirty solution ;) , you could just implement ControlValueAccessor in the CustomInputWrapperComponent also.由于您不想要一个肮脏的解决方案 ;) ,您也可以在CustomInputWrapperComponent实现ControlValueAccessor That way any change in the parent will be reflected in the child, any change in the child will be reflected in the parent as well with just few lines of code.这样,父项中的任何更改都将反映在子项中,子项中的任何更改也将反映在父项中,只需几行代码。

Wrapper Component包装组件

@Component({
  selector: 'custom-input-wrapper',
  template: '<custom-input [formControl]="value"></custom-input>',
  providers: [{
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(() => CustomInputWrapperComponent),
    multi: true,
  }]
})
export class CustomInputWrapperComponent implements AfterViewInit, ControlValueAccessor  {
  public value = new FormControl();

  constructor() { }

  ngAfterViewInit() {
    this.value.valueChanges.subscribe((x) => {
      this.onChangeFn(x);
    });
  }

  writeValue(value: number): void {
    this.value.setValue(value);
  }

  registerOnChange(fn: (_: any) => void): void {
    this.onChangeFn = fn;
  }

  registerOnTouched(fn: any): void {
  }

 onChangeFn = (_: any) => { };
}

Parent Template父模板

<form [formGroup]="form"><custom-input-wrapper formControlName="someInput"></custom-input-wrapper></form> <p>value is: {{formVal | json}}</p>

I have made a stackbitz demo here - https://stackblitz.com/edit/angular-csaxcz我在这里做了一个 stackbitz 演示 - https://stackblitz.com/edit/angular-csaxcz

you cannot use formControlName on custom-input-wrapper because it doesn't implement ControlValueAccessor .您不能在custom-input-wrapper上使用formControlName ,因为它没有实现ControlValueAccessor implementing ControlValueAccessor on custom-input-wrapper might be a solution but it seems to be overkill.custom-input-wrapper上实现ControlValueAccessor可能是一个解决方案,但它似乎有点矫枉过正。 Instead pass the control from formGroup to custom-input-wrapper as an @Input() and pass the inputed formControl to custom-input而是将控件从 formGroup 作为@Input()传递给custom-input-wrapper并将输入的 formControl 传递给custom-input

app.component应用组件

@Component({
  selector: 'my-app',
  template: `<form [formGroup]="form"><custom-input-wrapper [formCtrl]="form.get('someInput')"></custom-input-wrapper></form> <p>value is: {{formVal | json}}</p>`
})
export class AppComponent {
  form = this.fb.group({
    someInput: [],
  });

  get formVal() {
    return this.form.getRawValue();
  }

  constructor(private fb: FormBuilder) { }
}

custom-input-wrapper.component custom-input-wrapper.component

@Component({
  selector: 'custom-input-wrapper',
  template: '<custom-input [formControl]="formCtrl"></custom-input>',
})
export class CustomInputWrapperComponent {
  @Input() formCtrl: AbstractControl;
  constructor() { }
}

here is a working demo https://stackblitz.com/edit/angular-3lrfqv这是一个工作演示https://stackblitz.com/edit/angular-3lrfqv

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM