Angular2 ControlValueAccessor

时间:2016-06-03 08:02:31

标签: binding angular components controls

我试图了解ControlValueAccessor如何正确工作。

我用两种不同的控制组件研究了它的行为:

  • 第一个是假设提供一个原始值:一个数字。
  • 第二个提供了一个复杂的对象。

简而言之:

class FirstControlComponent implements ControlValueAccessor {
    // ...
    value:number = 10;
    writeValue(value: number) {
        this.value = value;
    }
    // ...
}

class SecondControlComponent implements ControlValueAccessor {
    // ...
    value:any = {};
    writeValue(value: any) {
        this.value = value;
    }
    // ...
}

ControlValueAccessor界面仅指定' setter': writeValue ,但没有' getter'。

因此,当我将Control绑定到SecondControlComponent时,类似于:

this.form = this.builder.group({
    controlName: this.builder.control(this.theObject) });

以及稍后的模板:

<second-component ngControl='controlName'> <second-component>

一切正常,因为在init上调用了writeValue并引用了现有的theObject对象,因此控件修改了对象的同一个实例(希望我明白)

BUT:如果我使用FirstControlComponent执行完全相同的操作,因为该值不作为参考传递(因为它是原始的),并且< strong>因为 ControlValueAccessor不提供&#39; setter&#39;我的控件中的值和主机组件中的值不保持同步...

这是否意味着我们必须将Object而不是原语传递给实现ControlValueAccessor的自定义控件?我猜不,所以我想我一定是误会了......:)

我正确地使用它吗?

欢迎任何提示!

谢谢!

2 个答案:

答案 0 :(得分:1)

我不清楚你在这里尝试做什么,但ControlValueAccessor是你需要为你的元素注册的实体。这样的事情:

const CUSTOM_VALUE_ACCESSOR = new Provider(
    NG_VALUE_ACCESSOR, {useExisting: forwardRef(() => LabelsValueAccessor), multi: true});

@Directive({
  (...)
  providers: [CUSTOM_VALUE_ACCESSOR]
})
export class LabelsValueAccessor implements ControlValueAccessor {
  (...)
}

然后它将获取部分值更新(来自组件和模板)。在输入中的组件中设置值时(例如),在值访问器中调用writeValue方法。如果要更新值访问器的输入值,则需要利用Angular2注册的onChange回调

有关更多详细信息,请参阅此文章(与#34; NgModel兼容的组件&#34;部分):

答案 1 :(得分:0)

在“父组件”中,我希望输出类似{someOtherControlName: any, controlName: number}的形式,因此“子”形式的输出必须是数字。还要以更新形式(例如),必须正确设置formControl的parentform.patchValue({someOtherControlName: '', controlNmae: 3})

@Component({
selector: 'parent',
template: `
    <div [formGroup]="form">
    <second-component [formControl]='form.controlName'> <second-component>
    <div formControlName="someOtherControlName"></div>
 export class ParentComponent {
    parentForm = new FormGroup({
       controlName: new FormControl(),  <<<<<======important
       someOtherControlName: new FormControl()
    })
}

在ControlValueAccessor中:

@Component({
selector: 'counter',
template: `
    <div class="number-input" [formGroup]="form">
        <input type="text" class="form-control text-center" formControlName="counter" />
</div>
`,
styleUrls: ['counter.component.scss'],
providers: [
    {
        provide: NG_VALUE_ACCESSOR,
        useExisting: CounterComponent,
        multi: true,
    },
],
})
 export class CounterComponent implements ControlValueAccessor, OnDestroy, OnInit {
   form = new FormGroup({
    counter: new FormControl(0, Validators.required),
   })

    private onChange: (value: any) => void = value => {}
    private onTouched: () => void = () => {}
    myValue: number
    private onDestroy$: Subject<void> = new Subject()
    ngOnInit() {
    this.form.valueChanges
        .pipe(
            tap(value => {
                this.onChange(typeof value === 'number' ? value : value.counter) <<<< important
                this.onTouched()
            }),
            takeUntil(this.onDestroy$)
        )
        .subscribe()
   }

   }
   writeValue(value: number | { counter: number }) {
    this.myValue = typeof value === 'number' ? value : value.counter
    this.form.setValue({ counter: value > 0 ? value : 0 })
    }
   registerOnChange(fn: () => {}) {
    this.onChange = fn
   }
   registerOnTouched(fn: () => {}) {
    this.onTouched = fn
   }
   setDisabledState?(isDisabled: boolean): void {
    // when the parent updates the
    // state of the form control

      if (isDisabled) {
        this.form.disable()
      } else {
        this.form.enable()
      }
   }
   ngOnDestroy(): void {
     this.onDestroy$.next()
     this.onDestroy$.complete()
   }