Search code examples
angularangular-materialangular2-forms

Dynamic form with ContentChildren


I am trying to create a dynamic form with ContentChildren, it is done, but I have 2 problems

  1. Without Material: I can't capture the input values when submitting, I don't know how to do it
  2. With Material: I can't capture the references to the inputs, adding "mat-form-field" made things complicated.

I leave an example in stackblitz. Thx!


Solution

  • In order to capture and watch for input values I would suggest you to add ngModel directive to all your input controls inside your dynamic form. This will automatically create an instance of FormControl for each input which you can add to your dynamic FormGroup:

    Note: you can also get input value by accessing inputElement.value but you have to listen for input event in order to watch for the changes

    html

    <filter-form (onSubmit)="onSubmit($event)">
        <input type="text" placeholder="0.00" propertyName="1" ngModel/>
        <input type="text" placeholder="0.00" propertyName="2" ngModel/>
        <input type="text" placeholder="0.00" propertyName="3" ngModel/>
        <mat-form-field>
            <input matInput type="text" placeholder="0.00" propertyName="4" ngModel/>
        </mat-form-field>
    </filter-form>  
    

    You can grab that NgModel instance by injecting it into your custom directive:

    @Directive({
      selector: "input"
    })
    export class FocusDirective {
    
      @Input() propertyName: string;
    
      constructor(public ngModel: NgModel) {}
    }
    

    Now, you can add FormControl instances to your formGroup:

    ngAfterContentInit(): void {
      console.log("ngAfterContentInit::input is: ", this.items);
      this.items.toArray().forEach(x => {
        this.formGroup.addControl(
          x.propertyName,
          x.ngModel.control <--------------------------- this one
        );
      });
    }
    

    In order to get all ContentChildren regardless of the nesting level you have to use descendants option:

    @ContentChildren(FocusDirective, { descendants: true }) items: QueryList<FocusDirective>;
    

    Forked Stackblitz