Wrapping angular reactive form component with validator

后端 未结 2 616
孤街浪徒
孤街浪徒 2021-02-06 15:04

Working with angular 7 and Bootstrap 4, I want to wrap my bootstrap 4 inputs in a custom component in order to reduce the boilerplate in my templates.

I want that the fi

相关标签:
2条回答
  • 2021-02-06 15:09

    The "key" is using viewProvider. You use a @Input set to give value to a formControl, see stackblitz. The "magic" is that if equal refered to formControl in the "children" or form.get('input1') in the parent

    @Component({
      selector: 'app-form-control',
      template: `
      <div class="form-group row">
      <label class="col-2 col-form-label">{{label}}</label>
      <div class="col-10">
        <input class="form-control" placeholder="{{placeholder}}"
        [formControl]="formControl" autocomplete="nope"/>
      </div>
      </div>
      <!--you can control the properties of formControl-->
      {{formControl.valid}}{{formControl.touched}}}
    `,
    viewProviders: [{ provide: ControlContainer, useExisting: FormGroupDirective }]})
    export class HelloComponent {
    
      formControl: FormControl;
    
      constructor(private parentF: FormGroupDirective) { }
    
      @Input() 
      set controlName(value) {
        this.formControl = this.parentF.form.get(value) as FormControl
      }
    
      @Input() label: string;
      @Input() placeholder: string;
    
    }
    

    And call the component this way:

    <form [formGroup]="myForm" (submit)="submit(myForm.value)">
        <app-form-control label="Lastname" placeholder="Lastname" controlName="lastName"></app-form-control>
    </form>
    

    Update well, (after a year) take account the stackblitz was erroneous. when you (click) in buttons create a new Form:

     this.form=this.createForm({note:'lll'})
    

    This "break" the relationship between the component and the form because the relation is about the older form -only change if change the @Input "nameControl". So the correct is use a setValue to give a new value to the form.

    0 讨论(0)
  • 2021-02-06 15:14

    You can implement by implementing ControlValueAccessor. Lets do the step by step process by building a TextBoxComponent.

    step 1: Creating NG_VALUE_ACCESSOR for textfield as TEXTBOX_VALUE_ACCESSOR.

    const TEXTBOX_VALUE_ACCESSOR = {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => TextBoxComponent),
      multi: true,
    }; 
    

    step 2: implemnting ControlValueAccessor to our component TextBoxComponent.

    export class TextBoxComponent implements ControlValueAccessor{
    ...
    ...
    }
    

    step 3: define unimplemnted methods of ControlValueAccessor. The detailed code of TextBoxComponent as below.

    @Component({
      selector: "text-box",
      template: `
    
        <div class="form-group row">
          <label class="col-2 col-form-label">{{label}}</label>
          <div class="col-10">
            <input class="form-control" placeholder="{{placeholder}}" [(ngModel)]="inputValue" />
          </div>
        </div>
      `,
      providers: [TEXTBOX_VALUE_ACCESSOR],
    })
    
    export class TextBoxComponent implements ControlValueAccessor {
      private _inputValue: any = '';
      private _onTouchedCallback: () => {};
      private _onChangeCallback: (_:any) => {};
    
      @Input("label") label: string = "Your Label";
      @Input("placeholder") placeholder: string = "Your Placeholder";
    
    
      get inputValue(): any {
        return this._inputValue;
      }
      set inputValue(value: any) {
        if (value !== this._inputValue) {
          this._inputValue = value;
          this._onChangeCallback(value);
        }
    
        this._onTouchedCallback();
    
      }
    
      //From ControlValueAccessor interface
      writeValue(value: any) {
        this._inputValue = value;
      }
    
      //From ControlValueAccessor interface
      registerOnChange(fn: any) {
        this._onChangeCallback = fn;
      }
    
      //From ControlValueAccessor interface
      registerOnTouched(fn: any) {
        this._onTouchedCallback = fn;
      }
    }
    

    How to use :

    <form [formGroup]="formGroup">
      <text-box formControlName="textboxControl" label="My Label" placeholder="My Placeholder"></text-box>
    
      <pre>{{formGroup.value | json}}</pre>
    </form>
    

    The complete code is available in stackblitz.

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