Home > Enterprise >  How to build the Nested Reactive Form structure using angular based on the JSON Data provided
How to build the Nested Reactive Form structure using angular based on the JSON Data provided

Time:05-04

I'm new to angular. Below is the JSON data I'm getting from GET call in postman. I'm trying to build the form using this

  data = {
    headline: [
      {
        language: 'en',
        headlineText: 'example headline',
      },
    ],
    bodyText: [
      {
        language: 'en',
        bodyText: 'example bodytext',
      },
    ],
    location: {
      name: 'mkontheway',
      openingHours: [
        {
          day: 'Mon-frd',
          timing: '10.00PM-9AM',
        },
      ],
      address: {
        postCode: 'test',
        country: 'test',
      },
    },
  };

But here Day and Timing fields are repeating 2 times. I'm not able to get the control of these two fields correctly because of nested form structure. Please someone suggest me the solution to build a complex nested form structure in angular.

Thanks in advance.

Here is the stack blitz link

Angular Nested Form

Here there are nested form groups and form arrays are there. Please suggest me how to achieve this nested form implementation using angular reactive forms

I took the reference from this stack blitz example Reference Stack blitz

CodePudding user response:

You only need create an empty formArray in openingHours in the location

   this.createAppForm = this.fb.group({
      ...
      location: this.fb.group({
        name: ['', Validators.required],
        openingHours: this.fb.array([]), //<--this is an "empty" formAray
        address: this.fb.group({
           ...
        }),
      }),
    });

But (always there're a but) seeing your code you has many repetitions to create the differents FormGroups. And repeat code makes we enter in trouble early.

So we are going to create functions that return the formGroups that compouned your FormGroup

Imagine you has some like

  setHeadLine(data: any = null) {
    data = data || { language: null, headlineText: null };
    return this.fb.group({
      headlineText: data.headlineText,
      language: data.language,
    });
  }

  setBodyText(data: any = null) {
    data = data || { bodyText: null, language: null };
    return this.fb.group({
      bodyText: data.bodyText,
      language: data.language,
    });
  }

  setAddress(data: any = null) {
    data = data || { postCode: null, country: null };
    return this.fb.group({
      postCode: [data.postCode, Validators.required],
      country: [data.country, Validators.required],
    });
  }

  setOpeningHour(data: any = null) {
    data = data || { day: null, timing: null };
    return this.fb.group({
      day: [data.day, Validators.required],
      timing: [data.timing, Validators.required],
    });
  }

  setLocation(data: any = null) {
    console.log(data);
    data = data || { name: null, openingHours: null, address: null };
    return this.fb.group({
      name: [data.name, Validators.required],
      openingHours: this.fb.array(
        data.openingHours
          ? data.openingHours.map((x) => this.setOpeningHour(x))
          : []
      ),
      address: this.setAddress(data.address),
    });
  }

See how the "setLocation" call to the function setAddress to create the formGroup adress and how openingHours is a this.fb.array. If data.openingHours is an array, convert the array of objects in an array of formGroups and create the formArray, else return an empty array

Finally you create a function that return your formGroup

  setFormGroup(data: any = null) {
    data = data || { headline: null, bodyText: null, location: null };
    return this.fb.group({
      headline: this.fb.array(
        data.headline ? data.headline.map((x) => this.setHeadLine(x)) : []
      ),
      bodyText: this.fb.array(
        data.bodyText ? data.bodyText.map((x) => this.setBodyText(x)) : []
      ),
      location: this.setLocation(data.location),
    });
  }

I this way you not repeat code. In the constructor you can write

 this.createAppForm = this.setFormGroup(this.data);

And when you add a FormGroup simply call the functions:

  addHeadline() {
    this.getHeadlineFormData().push(this.setHeadLine())
  }

  addBodyText() {
    this.getBodyTextFormData().push(this.setBodyText())
  }

  addOpeningHours() {
    this.getopeningHoursFormData().push(this.setOpeningHour())
  }

Well, (this last is only a suggestion) I think that the code becomes more clear using "getter", so if you replace

//replace
  getHeadlineFormData() {
    return <FormArray>this.createAppForm.get('headline');
  }
//by 
  get headlineFormData() {
    return <FormArray>this.createAppForm.get('headline');
  }

//replace
  getBodyTextFormData() {
    return <FormArray>this.createAppForm.get('bodyText');
  }
//by
  get bodyTextFormData() {
    return <FormArray>this.createAppForm.get('bodyText');
  }

//replace
  getLocationFormData() {
    return <FormArray>this.createAppForm.get('location');
  }
//by
  get locationFormData() {
    return <FormArray>this.createAppForm.get('location');
  }

//and replace
  getopeningHoursFormData() {
    return <FormArray>this.createAppForm.get('location')?.get('openingHours');
  }
//by
  get openingHoursFormData() {
    return <FormArray>this.createAppForm.get('location')?.get('openingHours');
  }

You can use this.headlineFormData instead this.getHeadlineFormData() -see that using a getter you don't write parentesis. and in html headlineFormData.controls instead getHeadlineFormData().controls -again see that using a "getter" you don't write parenthesis.

Your forked stackbliz

  • Related