2

I'm having a formArray namely "address" and this will be a dynamic, once the user clicks the "Add Address" button immediately one address form will add. Each address form has one radio button - User needs to select any of the address has a PRIMARY address (i.e., User needs to select one item in an array).

I refereed the following question but it now full-fill my requirement Angular formArray radio buttons in the said question each item has a group of radio button (i.e., selection within an item)

Working code is available in StackBlitz : https://stackblitz.com/edit/angular-reactiveform-radiobutton-in-arrayform

Source Code: AppComponent.ts

import { Component } from '@angular/core';
import { FormBuilder, FormGroup, FormArray, Validators } from '@angular/forms';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent  {
  name = 'Angular';
  public userForm: FormGroup;

  constructor(private _fb: FormBuilder) {
    this.userForm = this._fb.group({
      firstName: [],
      lastName: [],
      address: this._fb.array([this.addAddressGroup()])
    });
  }

  private addAddressGroup(): FormGroup {
    return this._fb.group({
      street: [],
      city: [],
      state: [],
      isPrimary: []
    });
  }

  get addressArray(): FormArray {
    return <FormArray>this.userForm.get('address');
  }

  addAddress(): void {
    this.addressArray.push(this.addAddressGroup());
  }

  removeAddress(index: number): void {
    this.addressArray.removeAt(index);
  }
}

Source Code: AppComponent.html

<form class="example-form" [formGroup]="userForm">
  <div>
    <mat-card class="example-card">
      <mat-card-header>
        <mat-card-title>Users Creation</mat-card-title>
      </mat-card-header>
      <mat-card-content>
        <div class="primary-container">
          <mat-form-field>
            <input matInput placeholder="First Name" value="" formControlName="firstName">
          </mat-form-field>
          <mat-form-field>
            <input matInput placeholder="Last Name" value="" formControlName="lastName">
          </mat-form-field>
        </div>
        <div formArrayName="address">
          <div class="address-container" *ngFor="let group of addressArray.controls; let i = index;"
            [formGroupName]="i">
            <fieldset>
              <legend>
                <h3>Address: {{i + 1}}</h3>
              </legend>
              <mat-radio-button class="example-radio-button" value="true" checked="true" formControlName="isPrimary">
                Primary
              </mat-radio-button>
              <div>
                <mat-form-field>
                  <input matInput placeholder="Street" value="" formControlName="street">
                </mat-form-field>
                <mat-form-field>
                  <input matInput placeholder="City" value="" formControlName="city">
                </mat-form-field>
                <mat-form-field>
                  <input matInput placeholder="State" value="" formControlName="state">
                </mat-form-field>
              </div>
            </fieldset>
          </div>
        </div>
        <div class="form-row org-desc-parent-margin">
          <button mat-raised-button (click)="addAddress()">Add more address</button>
        </div>
      </mat-card-content>
    </mat-card>
  </div>
</form>
<mat-card class="pre-code">
  <mat-card-header>
    <mat-card-title>Users Information</mat-card-title>
  </mat-card-header>
  <mat-card-content>
    <pre>{{userForm.value | json}}</pre>
  </mat-card-content>
</mat-card>

enter image description here

Kindly assist me how to select an address as a PRIMARY out of N number of address. Only one of the address property "isPrimary" should be true other items and all should be false

B.Balamanigandan
  • 4,713
  • 11
  • 68
  • 130
  • I think you to do like, when radio button checked event fire you have to pass in in ts file and make that only checked and perform loop through of added address and mark other radio as unchecked. – Paresh Gami Mar 28 '19 at 05:46
  • @PareshGami - Yeah but I'm waiting for any direct way is there or what otherwise code wise implementation is the only way to do that. – B.Balamanigandan Mar 28 '19 at 05:52
  • I am having similar issue. How did you solve this situation? @B.Balamanigandan – Kalpesh Patel Aug 20 '19 at 20:25

1 Answers1

2

In a new project, I achieved this just using material components, without involving events / loops... the key is just to use the name property of the mat-radio-group to make all the iterations of the radio group behave as one.

<mat-radio-group [formControl]="userForm.controls.primary" name="primary">

I have also used a different approach for the "isPrimary" check. Instead of having a boolean property in every address instance, I used a single property "primary address index" inside the user model, so when you click on a radio button, the value of the primary field is updated using the index of the current element ([value]="i")

<mat-radio-button [value]="i">Primary</mat-radio-button>

this is my entire form

<form [formGroup]="userForm" (ngSubmit)="onSubmit()">
    name
    <input formControlName="name" /><br>
    email
    <input formControlName="email" /><br>
    VAT
    <input formControlName="VAT" /><br>

    <button (click)="addNewAddress()">Add new address</button><br>

    <div formArrayName="addresses">
        <div *ngFor="let address of userForm.get('addresses').controls; let i=index">
            <h3>ADDRESS {{i+1}}: </h3>
            primary
            <mat-radio-group [formControl]="userForm.controls.primary" name="primary">
                <mat-radio-button [value]="i">Primary
                </mat-radio-button>
            </mat-radio-group>


            <div [formGroupName]="i">
                street
                <input formControlName="street" /><br>
                city
                <input formControlName="city" /><br>
                country
                <input formControlName="country" /><br>
                zip
                <input formControlName="zip" /><br>

                <button (click)="deleteAddress(i)">
                    Delete <address></address>
                </button>
            </div>
        </div>
    </div>
    <button type="submit" [disabled]="userForm.pristine || !userForm.valid">Save</button>
</form>

here the ts relevant parts:

initForm() {
   this.detailForm = this.fb.group({
        name: ['', Validators.required],
        VAT: ['', Validators.required],
        fiscalCode: ['', Validators.required],
        legalEntity: ['',],
        email: ['', Validators.email],
        primary: [0, Validators.required], //when creating a new customer, set first address as primary
        addresses: this.fb.array([]),
      });
}

addNewAddress(addr: Address): void {
    let control = <FormArray>this.detailForm.controls.addresses;
    control.push(
      this.fb.group({
        street: ['', Validators.required],
        city: ['', Validators.required],
        country: ['', Validators.required],
        zip: [''],
      })
    );
  }

hope this can help!

Geko
  • 33
  • 6