Search code examples
angularangular-forms

Angular 2 Reactive Form - Reuse individual form control


In my Angular app I have a complex entity that can be created in multiple ways: manually, by uploading json files, by importing from another url, etc.

For each case described above, I have build a separate form, using Reactive Forms, in separate components.

All the other form fields are different, except the field for the atrribute called name.

The formControl attached to the name attribute has the following definition:

name: new FormControl(this.initialValue, [
        Validators.required,
        Validators.minLength(3),
        Validators.maxLength(10)
      ])

Now, I don't like that I have to repeat the same definition for this form control on every reactive form constructor, which, as I said before, are in separate components.

Here are some code snippets:

this.formA = new FormGroup({
      name: new FormControl(this.initialValue, [
        Validators.required,
        Validators.minLength(3),
        Validators.maxLength(10)
      ]),
      fooControl1: new FormControl(''),
      fooControl2: new FormControl('')
    });
this.formB = new FormGroup({
      name: new FormControl(this.initialValue, [
        Validators.required,
        Validators.minLength(3),
        Validators.maxLength(10)
      ]),
      barControl: new FormControl('')
    });

I have build a simple Stackblitz example: https://stackblitz.com/edit/angular-tyjnlq. (for simplification, in the example the forms are in same component)

Question: How can I save this form control and include it in every form?


Solution

  • To build reusable form, we have to use ControlValueAccessor API.

    What is CVA?

    According to Angular documentation, ControlValueAccessor Defines an interface that acts as a bridge between the Angular forms API and a native element in the DOM.

    @Component({
      selector: "app-nameform",
      templateUrl: "./nameform.component.html",
      styleUrls: ["./nameform.component.css"],
      providers: [
        {
          provide: NG_VALUE_ACCESSOR,
          useExisting: NameformComponent,
          multi: true
        },
         {
          provide: NG_VALIDATORS,
          useExisting: NameformComponent,
          multi: true
        }
      ]
    })
    export class NameformComponent implements OnInit, ControlValueAccessor {
      form: FormGroup;
      onTouched: any;
      onChange:any;
      constructor() {
        this.form = new FormGroup({
          name: new FormControl("", [
            Validators.required,
            Validators.minLength(3),
            Validators.maxLength(10)
          ])
        });
        this.form.valueChanges.subscribe(value=>{
          this.onChange(value);
          this.onTouched();
        })
      }
    
      ngOnInit() {}
      writeValue(value: any) {
       value && this.form.setValue(value, {emitEvent:false});
      }
    
      registerOnChange(fn: any) {
        this.onChange = fn;
      }
    
      registerOnTouched(fn: any) {
        this.onTouched = fn;
      }
        get controls() {
        return this.form.controls;
      }
    
      validate(control:AbstractControl){
        return this.form.valid;
      }
    }
    

    Example

    For Detailed explanationCheck this Blog