Attempting to extend FormControlDirective to implement my own FormControl directive results in faulty binding

前端 未结 2 953
太阳男子
太阳男子 2021-01-03 11:04

I\'m trying to inverse the way forms controls are registering themselves onto a FormGroup, so that instead of having to

@Component({..., templ         


        
相关标签:
2条回答
  • 2021-01-03 11:41

    You are almost there. There is one more trick though. There isn't DefaultValueAccessor for that input element, thus constructor arguments are populate with null value.

    The formControl \ formControlName selectors appear in one more place - the value accessor. In order your directive to work you should implement all default value accessors for the hybridFormControl directive ( following the pattern for the built-in directives).

    P.S I believe the provider of your directive should be corrected to

    providers: [{
        provide: NgControl, //<-- NgControl is the key
        useExisting: forwardRef(() => HybridFormControlDirective)
    }]
    
    0 讨论(0)
  • 2021-01-03 11:44

    I ran into the same issue. Odd that there aren't more Stackoverflow posts about this. The above answer did not work for me, but this is how I solved it in the case that there are more out there.

    @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;
      }
    }
    

    And this hybrid component could be used like:

    @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({});
      }
    }
    

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

    0 讨论(0)
提交回复
热议问题