Creating nested form groups using Angular reactive forms
Asked Answered
D

2

10

I've ran into a problem when trying to implement a nested angular FormGroup.

I'm using a specific scheme (interfaces between client and server) that describes the form inputs. I would like to create a FormGroup with certain validations, and find the easiest and efficient way for initializing the FormGroup with the scheme object.

The scheme object properties are object themselves, so foreach one of them I would like to create its own FormGroup. Also, the component that holds the main FormGroup contains child components which should be connected to the inner FromGroups (the keys of the scheme object).

For example:

MySchema: {
  velocity: 
  {
    speed: number,
    direction: number
  },
  position: 
  {
    x: number,
    y: number
  }
}

The main form should have 2 inner components - velocity component and position component.

Each child component should be connected to a child formGroup (velocity-component to the velocityGrouop and such..) and show the relevant validations.

The main form should be connected to the main FormGroup.

This is a small example, in our project the schema is much bigger and writing all the inner elements manually seems wrong.

I'm hoping you will be able to help me find a better way to do it.

Thanks.

Update

Maybe my question was not clear enough.. Is there a way to convert a big object to angular form groups / form control?

Each of my forms are containing objects that I received from the server and writing each element in fb manually seems wrong..

Thanks again and for everyone who already answered

Durrell answered 27/8, 2018 at 11:15 Comment(2)
This might help https://mcmap.net/q/179409/-when-to-use-formgroup-vs-formarrayCantlon
Hi, we (at work) have been working on that topic and I gave a proper answer you may want to check here https://mcmap.net/q/833307/-angular-composite-controlvalueaccessor-to-implement-nested-form :)Chartism
F
14

Angular Reactive Form Supports Multiple Form Groups.

Component:

this.MySchemaFrom = this.fb.group({
        velocity: this.fb.group({
            speed: ['', Validators.required],
            direction: ['', Validators.required]
        }),
        position: this.fb.group({
            x: ['', Validators.required],
            y: ['', Validators.required]
        })
    });

Template:

<form [formGroup]="MySchemaFrom">
        <div formGroupName="velocity">
                <legend> velocity </legend>
            <div class="form-group">
                <label for="name">Speed</label>
                <input 
                    type="text" 
                    class="form-control" 
                    id="name" 
                    formControlName="speed">
            </div>
            <div class="form-group">
                <label for="email">Direction</label>
                <input 
                    type="text" 
                    class="form-control" 
                    id="email" 
                    formControlName="direction">
            </div>
        </div>
        <div formGroupName="position">
                <legend> position </legend>
            <div class="form-group">
                <label for="name">X</label>
                <input 
                    type="text" 
                    class="form-control" 
                    id="name" 
                    formControlName="x">
            </div>
            <div class="form-group">
                <label for="email">Y</label>
                <input 
                    type="text" 
                    class="form-control" 
                    id="email" 
                    formControlName="y">
            </div>
        </div>

        <button (click)="submit()">Submit</button>
    </form>
Fiducial answered 27/8, 2018 at 11:31 Comment(3)
Yes I know that something like that is possible. My problem is that the interface I received is very big with a lot of nested objects and creating this fb manually feels wrong. Is there a way to automate it? Maybe with some kind of recursive? Or something built in by angular?Durrell
It would also be nice to have type-safety on the entire form and nested form-groups. For example, you could clear a section of the form by doing this.MySchemaForm.get('position').set({x: '', y: ''}) and if for whatever reason the interface for position would change in the future, e.g. introducing z: string, then you wouldn't miss the spot, because .set would expect the object property z and would not compile otherwise. Is this achievable in modern Angular somehow?Tideway
What is the situation if the position: this.fb.group() is located in a custom FormGroup based component?Quasimodo
M
0
<section class="gradient-custom">
    <div class="container py-5 h-100">
      <div class="row justify-content-center align-items-center h-100">
        <div class="col-12 col-lg-9 col-xl-7">
          <div class="card shadow-2-strong card-registration" style="border-radius: 15px;">
            <div class="card-body p-4 p-md-5">
              <h3 class="mb-4 pb-2 pb-md-0 mb-md-5">Registration Form</h3>
              <form [formGroup]="registrationFrom">
  
                <div class="row">
                  <div class="col-md-6 mb-4">
  
                    <div class="form-outline">
                      <input type="text" id="firstName" class="form-control form-control-lg" formControlName="firstName" />
                      <label class="form-label" for="firstName">First Name</label>
                    </div>
  
                  </div>
                  <div class="col-md-6 mb-4">
  
                    <div class="form-outline">
                      <input type="text" id="lastName" class="form-control form-control-lg" formControlName="lastName"/>
                      <label class="form-label" for="lastName">Last Name</label>
                    </div>
  
                  </div>
                </div>
  
                <div class="row">
                  <div class="col-md-6 mb-4 d-flex align-items-center">
  
                    <div class="form-outline datepicker w-100">
                      <input type="text" class="form-control form-control-lg" id="birthdayDate" formControlName="DOB" />
                      <label for="birthdayDate" class="form-label">Birthday</label>
                    </div>
  
                  </div>
                  <div class="col-md-6 mb-4">
  
                    <h6 class="mb-2 pb-1">Gender: </h6>
  
                    <div class="form-check form-check-inline">
                      <input class="form-check-input" type="radio"
                        value="male" checked formControlName="gender" />
                      <label class="form-check-label">Male</label>
                    </div>
  
                    <div class="form-check form-check-inline">
                      <input class="form-check-input" type="radio"
                        value="female" formControlName="gender"/>
                      <label class="form-check-label">Female</label>
                    </div>
                  </div>
                </div>
  
                <div class="row">
                  <div class="col-md-6 mb-4 pb-2">
  
                    <div class="form-outline">
                      <input type="email" id="emailAddress" class="form-control form-control-lg" formControlName="email" />
                      <label class="form-label" for="emailAddress">Email</label>
                    </div>
  
                  </div>
                  <div class="col-md-6 mb-4 pb-2">
  
                    <div class="form-outline">
                      <input type="tel" id="phoneNumber" class="form-control form-control-lg" formControlName="phoneNumber"  />
                      <label class="form-label" for="phoneNumber">Phone Number</label>
                    </div>
  
                  </div>
                </div>
  

                <label for="" class="mt-3">Address : </label>
              <div class="px-5" formGroupName="address">
                <div class="form-group mt-3">
                    <label class="pb-2">Country</label>
                    <select class="form-select form-select-lg mb-3"  formControlName="country">
                        <option value="" disabled hidden>Select Country</option>
                      <option *ngFor="let country of countryList" value="{{country?.id}}">{{country?.value}}</option>
                    </select>,
                    
                  </div>
                <div class="form-group">
                    <label class="pb-2">City</label>
                    <input type="text"  class="form-control form-control-lg" formControlName="city">
                  </div>

                  <div class="form-group">
                    <label class="pb-2">Street</label>
                    <input type="text" class="form-control form-control-lg" formControlName="street">
                    
                  </div>

                  <div class="form-group">
                    <label class="pb-2">PinCode</label>
                    <input type="text" class="form-control form-control-lg" formControlName="pinCode">
                    
                  </div>
              </div>

  
                <div class="mt-4 pt-2">
                  <button class="btn btn-primary mx-1" (click)="register()">Register</button>
                  <button class="btn btn-success mx-1" (click)="setValuesToFrom()">SetValue</button>
                  <button class="btn btn-warning" (click)="reset()">Reset</button>
                </div>
  
              </form>
            </div>
          </div>
        </div>
      </div>
    </div>
  </section>
// make instance of queryBuilder in construtor and use below ts file code
registrationFrom = this.fb.group({
  firstName : [''],
  lastName : [''],
  DOB : [''],
  gender : ['male'],
  email : [''],
  phoneNumber : [''],
  address : this.fb.group({
    country : [''],
    city : [''],
    street : [''],
    pinCode : ['']
  })
})
.gradient-custom {
    /* fallback for old browsers */
    background: #f093fb;
    
    /* Chrome 10-25, Safari 5.1-6 */
    background: -webkit-linear-gradient(to bottom right, rgba(240, 147, 251, 1), rgba(245, 87, 108, 1));
    
    /* W3C, IE 10+/ Edge, Firefox 16+, Chrome 26+, Opera 12+, Safari 7+ */
    background: linear-gradient(to bottom right, rgba(240, 147, 251, 1), rgba(245, 87, 108, 1))
    }
    
    .card-registration .select-input.form-control[readonly]:not([disabled]) {
    font-size: 1rem;
    line-height: 2.15;
    padding-left: .75em;
    padding-right: .75em;
    }
    .card-registration .select-arrow {
    top: 13px;
    }
Macgregor answered 29/4, 2023 at 17:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.