Home > Net >  How To Create Dynamic FormGroups for Sections and check each section if form is valid
How To Create Dynamic FormGroups for Sections and check each section if form is valid

Time:11-25

I have Multiple Sections Each Containing Subsections and inside I have Questions that I need to validate, The Thing is:

I have a save button for each section.

Edit: I Don't know how many Sections I will have from Api and how many Subsections or Questions its all dynamic

how do I make: FormGroup for each Subsection. and inside Subsections I have array of questions(fields) that I need to have FormControls for. what I want to achieve is: SectionForm1.isValid save();, SectionForm2.isValid save()...

sections

I've tried doing this. but it adds formControl for all fields. Demo

CodePudding user response:

If you have to make components inside Forms you should create custom FormControls (you can read this article)

If you want a snippet to check if every control is valid you can do as follows:

checkValidity(): void {
  const findInvalidControls = () => {
    const invalid = [];
    const controls = ...this.form.controls;

    for (const name in controls) {
      if ((controls as any)[name]?.invalid) {
        invalid.push(name);
      }
    }
    return invalid;
  };

  const invalid = findInvalidControls();

  // Do something if "invalid.length" is > 0
}

In order for this to work, you have to make sure that when you create a FormControl, you set some validators, for example:

In your component.ts:

private control = new FormControl('') // Initialize the control with a value. Can be a value of '' if it's a string

private control = new FormControl('', [Validators.required]) // Set the validators in your control

private control = new FormControl<string>('', [Validators.required]) // Take advantages of typed FormControls

CodePudding user response:

Here is a simplified version of how I handled large forms with different sections and logic in a large project. Not sure, if it fits to your problem.

Create a parent component and a Subsection Component for each subsection including a own formgroup, save logic, etc.

<!-- Parent Component HTML -->
<subsection1-form #subsection1Form></subsection1-form>
<subsection2-form #subsection2Form></subsection2-form>
// Parent Component TS
@ViewChild('subsection1Form') subsection1Form: Subsection1FormComponent;
@ViewChild('subsection2Form') subsection2Form: Subsection2FormComponent;

/** Call this method to save all subsections */
save() {
  this.subsection1Form.form.markAllAsTouched();
  this.subsection2Form.form.markAllAsTouched();

  if (this.subsection1Form.valid && this.subsection2Form.valid ) {
    forkJoin([
      this.subsection1Form.submit(),
      this.subsection2Form.submit()
    ]).subscribe(/* ... */);
  }
}

I also created a simple interface for SubsectionForms and let the subsection components implement it.

export interface SubsectionForm {
  /** Make a request and return the response if it is successful. On errors return 
  Overservable<never>. */
    submit(): Observable<any>;
  }
}

And the SubsectionComponent would look like this:

@Component({
  selector: 'subsection1-form',
  templateUrl: './subsection1-form.component.html',
  styleUrls: ['./subsection1-form.component.scss']
})
export class Subsection1FormComponent implements OnInit, SubsectionForm {

  @ViewChild('form') form: NgForm;

  submit(): Observable<any> {
    // Do something
  }
}

This way, the subsections business logic is nicely encapsuled.

I think this is also pretty flexible:

  • Subsections can also returns anything else. Doesn't need to return an Observable.
  • You can add more validation either in subsections directly or in parent component if it is "cross-subsection" logic

For dynamic Forms you can replace the subsection components with a dynamic FormGroup-array.

// Parent Component TS
subsections: FormGroup[];

initializeSubsections(): void {
  // However, create/initialize/update the different forms dynamically
}

/** Call this method to save all subsections */
save() {
  this.subsections.forEach((form: FormGroup) => form.markAllAsTouched());

  if (this.subsections.every(({ valid }: FormGroup) => valid)) {
    // Do something with your subsections
  }
}

Maybe you can combine it, if you can create a generic subsection component.

<!-- in Parent Component HTML -->
<!-- you can also create a more complex array with additional input parameters, not only the formGroup -->
<subsection-component *ngFor="let form in subsections; index as i" [form]="form" (submit)="submit(i)"></subsection-component>
// Added in Parent Component TS
submit(index: number) {
  if (this.subsections[i].valid) {
    // Do something with subsection i
  }
}

CodePudding user response:

As i see you are using child component .To fix this issue .Firstly you have to use ControlvalueAccessor in your child component. click here . Here is eg. how to use this click here

  • Related