0

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

1 Answer 1

2

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

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.