尝试扩展FormControlDirective以实现我自己的FormControl指令会导致绑定错误

时间:2017-06-28 20:43:31

标签: angular angular2-forms angular2-di

我试图反转表单控件将自己注册到FormGroup的方式,这样就不必

@Component({..., template: `<input [formControl]="myControl"`}    
...

@Component({..., template: `<input [formControName]="myControlName"`}    
...

我可以

@Component({..., template: `<input myFormControl`}    
...

让我的指令为我创建并添加FormControl

最好用Plunker解释。

似乎没有用的是将视图绑定到表单模型,如您所见,更改输入不会更改表单模型值。

调试它表明没有valueAccessor注入构造函数(与直接使用基类FormControlDirective类不同)。

如果您想知道,我的最终目标是我将拥有一个@ViewChildren(MyFormDirective)的父自定义组组件,并将它们全部动态添加到其创建的表单中。

2 个答案:

答案 0 :(得分:2)

你快到了。还有一个技巧。该输入元素没有DefaultValueAccessor,因此构造函数参数填充null值。

formControl \ formControlName选择器出现在另一个地方 - the value accessor。为了使你的指令工作,你应该为hybridFormControl指令实现所有默认值访问器(遵循内置指令的模式)。

P.S我相信你的指令提供者应该更正

providers: [{
    provide: NgControl, //<-- NgControl is the key
    useExisting: forwardRef(() => HybridFormControlDirective)
}]

答案 1 :(得分:0)

我遇到了同样的问题。奇怪的是,没有更多关于此的Stackoverflow帖子。上面的答案对我不起作用,但是如果那里还有更多,这就是我解决的方法。

@Directive({
  selector: '[hybridFormControl]'
})
class HybridFormControl Directive extends FormControlName implements ControlValueAccessor, OnChanges {
  @Input('hybridFormControl') name: string;

  onChange;
  onTouched;

  constructor(
      @Optional() protected formGroupDirective: FormGroupDirective,
      @Optional() @Self() @Inject(NG_VALUE_ACCESSOR) valueAccessors: ControlValueAccessor[],
      private fb: FormBuilder,
      private renderer: Renderer2,
      private element: ElementRef
  ) {
    super(formGroupDirective, [], [], valueAccessors, null);
    this.valueAccessor = this;
  }

  ngOnChanges(changes: SimpleChanges): void {
    if (!this._registered) {
      // dynamically create the form control model on the form group model.
      this.formGroup = this.formGroupDirective.form;
      this.formGroup.registerControl(name, this.fb.control(''));
      this._registered = true;
    }

    // IMPORTANT - this ties your extended form control to the form control 
    // model on the form group model that we just created above. Take a look
    // at Angular github source code.
    super.ngOnChanges(changes); 
  }

  @HostListener('input', ['$event.target.value'])
  @HostListener('change', ['$event.target.value'])
  onInput(value): void {
    this.onChange(modelValue);
  }

  writeValue(value): void {
    const element = this.element.nativeElement;
    this.renderer.setProperty(element, 'value', value);
  }

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

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

这个混合组件可以像这样使用:

@Component({
  selector: 'app',
  template: `
    <form formGroup=[formGroup]>
      <input type="text" hybridFormControl="myName">
    </form>
  `
class AppComponent {
  formGroup: FormGroup

  constructor(fb: FormBuilder) {
    this.form = this.fb.group({});
  }
}

来源:https://github.com/angular/angular/blob/master/packages/forms/src/directives/reactive_directives/form_control_name.ts#L212