Dynamic Radio Buttons and Checkboxes in Ionic 8 and Angular 20 Using FormArray

by Didin J. on Jun 11, 2025 Dynamic Radio Buttons and Checkboxes in Ionic 8 and Angular 20 Using FormArray

Learn how to build dynamic forms in Ionic 8 and Angular 20 with radio buttons and checkboxes using FormArray and Reactive Forms for flexible data handling.

Building dynamic forms with radio buttons and checkboxes is a common need in modern apps, especially for surveys, quizzes, or preference settings. In this updated tutorial, you’ll learn how to implement reactive forms in Ionic 8 and Angular 20  FormArray to dynamically manage user input. We’ll walk through how to render questions with radio or checkbox options, capture selections, and display the results—all with the latest Angular best practices and Ionic UI components.


The following tools, frameworks, libraries, and modules are required for this tutorial:

  • Node.js (Recommended version)
  • Ionic 8
  • Angular 20
  • Express and MongoDB API
  • Terminal or Node command line
  • IDE or Text Editor

 


1. Initialize Ionic 8 + Angular 20 Project

Type this command to install Ionic 8 and create an Ionic project.

npm install -g @ionic/cli
ionic start ionic8-radio blank --type=angular --capacitor
cd ionic8-radio

Update all dependencies and devDependencies:

"dependencies": {
    "@angular/animations": "20.0.2",
    "@angular/common": "20.0.2",
    "@angular/compiler": "20.0.2",
    "@angular/core": "20.0.2",
    "@angular/forms": "20.0.2",
    "@angular/platform-browser": "20.0.2",
    "@angular/platform-browser-dynamic": "20.0.2",
    "@angular/router": "20.0.2",
    "@ionic/angular": "^8.0.0",
    "ionicons": "8.0.9",
    "rxjs": "~7.8.0",
    "tslib": "^2.3.0",
    "zone.js": "~0.15.0"
  },
  "devDependencies": {
    "@angular-devkit/build-angular": "20.0.1",
    "@angular-eslint/builder": "20.0.0",
    "@angular-eslint/eslint-plugin": "20.0.0",
    "@angular-eslint/eslint-plugin-template": "20.0.0",
    "@angular-eslint/schematics": "20.0.0",
    "@angular-eslint/template-parser": "20.0.0",
    "@angular/cli": "20.0.1",
    "@angular/compiler-cli": "20.0.2",
    "@angular/language-service": "20.0.2",
    "@ionic/angular-toolkit": "^12.0.0",
    "@types/jasmine": "~5.1.0",
    "@typescript-eslint/eslint-plugin": "^8.18.0",
    "@typescript-eslint/parser": "^8.18.0",
    "eslint": "^9.16.0",
    "eslint-plugin-import": "^2.29.1",
    "eslint-plugin-jsdoc": "50.8.0",
    "eslint-plugin-prefer-arrow": "1.2.2",
    "jasmine-core": "5.8.0",
    "jasmine-spec-reporter": "7.0.0",
    "karma": "~6.4.0",
    "karma-chrome-launcher": "~3.2.0",
    "karma-coverage": "~2.2.0",
    "karma-jasmine": "~5.1.0",
    "karma-jasmine-html-reporter": "~2.1.0",
    "typescript": "5.8.3"
  },

Re-download all dependencies:

npm install

Angular and Ionic versions are updated automatically. You can confirm with:

ionic info
ng version

Next, run the Ionic 4 application for the first time using this command.

ionic serve

Dynamic Radio Buttons and Checkboxes in Ionic 8 and Angular 20 Using FormArray - ionic serve


2. Install Forms Module

Type this command to install the Angular Forms Module.

npm install @angular/forms

We will use ReactiveFormsModule and FormsModule directly in the component or page later.


3. Generate Details & Edit Pages

Type this command to generate Details and Edit pages.

ionic g page details
ionic g page edit

Update details/details.page.ts:

import { FormsModule, ReactiveFormsModule } from '@angular/forms';

@Component({
  ...
  imports: [IonContent, IonHeader, IonTitle, IonToolbar, CommonModule, FormsModule, ReactiveFormsModule,]
})

Update edit/edit.page.ts:

import { FormsModule, ReactiveFormsModule } from '@angular/forms';

@Component({
  ...
  imports: [IonContent, IonHeader, IonTitle, IonToolbar, CommonModule, FormsModule, ReactiveFormsModule]
})


4. Create Questionnaire Form with FormArray

In src/app/home/home.page.ts:

import { CommonModule } from '@angular/common';
import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, FormArray, Validators, FormControl, FormsModule, ReactiveFormsModule } from '@angular/forms';
import { Router } from '@angular/router';
import { IonHeader, IonToolbar, IonTitle, IonContent, IonCheckbox, IonButton, IonItem, IonLabel, IonList, IonRadio } from "@ionic/angular/standalone";

@Component({
  selector: 'app-home',
  templateUrl: './home.page.html',
  standalone: true,
  imports: [CommonModule, IonRadio, IonList, IonLabel, IonItem, IonButton, IonCheckbox, IonContent, IonHeader, IonTitle, IonToolbar, FormsModule, ReactiveFormsModule]
})
export class HomePage implements OnInit {
  qsForm: FormGroup = this.fb.group({
    answers: this.fb.array([])
  });

  questions = [
    { title: 'Single choice question', type: 'radio', options: ['A', 'B', 'C'] },
    { title: 'Multiple answers question', type: 'checkbox', options: ['One', 'Two', 'Three'] }
  ];

  constructor(private fb: FormBuilder, private router: Router) { }

  ngOnInit() {
    const answerControls = this.questions.map(q =>
      q.type === 'radio'
        ? this.fb.control('', Validators.required)
        : this.fb.group(q.options.reduce((acc: { [key: string]: any }, opt) => {
          acc[opt] = this.fb.control(false);
          return acc;
        }, {}))
    );

    this.qsForm.setControl('answers', this.fb.array(answerControls));
  }

  buildCheckboxGroup(options: string[]): FormGroup {
    const group: { [key: string]: FormControl } = {};
    options.forEach(opt => group[opt] = this.fb.control(false));
    return this.fb.group(group);
  }

  get answersArray() {
    return this.qsForm?.get('answers') as FormArray;
  }

  onSubmit() {
    console.log(this.qsForm?.value);
    this.router.navigate(['/details'], {
      state: { answers: this.qsForm?.value, questions: this.questions }
    });
  }
}

  onSubmit() {
    console.log(this.qsForm?.value);
    this.router.navigate(['/details'], {
      state: { answers: this.qsForm?.value, questions: this.questions }
    });
  }
}

HTML (home.page.html):

<ion-header [translucent]="true">
  <ion-toolbar>
    <ion-title>
      Dynamic Radio and Checkbox
    </ion-title>
  </ion-toolbar>
</ion-header>

<ion-content [fullscreen]="true">
  <form [formGroup]="qsForm" (ngSubmit)="onSubmit()">
    <ng-container formArrayName="answers">
      <div *ngFor="let q of questions; let i = index">
        <h2>{{ q.title }}</h2>

        <!-- Single-choice (radio) -->
        <ion-list *ngIf="q.type==='radio'" [formControlName]="i" radio-group>
          <ion-item *ngFor="let opt of q.options">
            <ion-label>{{ opt }}</ion-label>
            <ion-radio [value]="opt"></ion-radio>
          </ion-item>
        </ion-list>

        <!-- Multiple-choice (checkboxes) -->
        <ng-container *ngIf="q.type==='checkbox'" [formGroupName]="i">
          <ion-item *ngFor="let opt of q.options">
            <ion-label>{{ opt }}</ion-label>
            <ion-checkbox [formControlName]="opt"></ion-checkbox>
          </ion-item>
        </ng-container>

      </div>
    </ng-container>
    <ion-button expand="full" type="submit">Submit</ion-button>
  </form>
</ion-content>


5. Display Answers on a Details Page

In details.page.ts:

import { Component, OnInit } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { IonContent, IonHeader, IonTitle, IonToolbar, IonList, IonItem } from '@ionic/angular/standalone';
import { Router } from '@angular/router';

@Component({
  selector: 'app-details',
  templateUrl: './details.page.html',
  styleUrls: ['./details.page.scss'],
  standalone: true,
  imports: [IonItem, IonList, IonContent, IonHeader, IonTitle, IonToolbar, CommonModule, FormsModule, ReactiveFormsModule,]
})
export class DetailsPage implements OnInit {

  questions: any[] = [];
  answers: any[] = [];

  constructor(private router: Router) {
    const nav = this.router.getCurrentNavigation();
    this.questions = nav?.extras.state?.['questions'];
    this.answers = nav?.extras.state?.['answers'];
  }

  ngOnInit() { }

  getSelectedOptions(q: any, i: number): string {
    if (q.type === 'radio') {
      return this.answers[i] || 'None';
    }

    if (q.type === 'checkbox') {
      const selected = q.options.filter((opt: string | number) => this.answers[i]?.[opt]);
      return selected.length > 0 ? selected.join(', ') : 'None';
    }

    return 'None';
  }

}

HTML template:

<ion-header [translucent]="true">
  <ion-toolbar>
    <ion-title>details</ion-title>
  </ion-toolbar>
</ion-header>

<ion-content [fullscreen]="true">
  <ion-list>
    <ion-item *ngFor="let q of questions; let i = index">
      <h3>{{ q.title }}</h3>
      <p>
        <span *ngIf="q.type==='radio'">{{ answers[i] }}</span>
        <span *ngIf="q.type==='checkbox'">
          {{ getSelectedOptions(q, i) }}
        </span>
      </p>
    </ion-item>
  </ion-list>
</ion-content>


6. Run and Test Dynamic Radio Buttons and Checkboxes

Run the Ionic app by typing this command.

ionic serve

And here's what the complete Ionic Angular dynamic radio buttons and checkboxes.

Dynamic Radio Buttons and Checkboxes in Ionic 8 and Angular 20 Using FormArray - test app

Summary

  • CLI & versions updated: Ionic 8 + Angular 20

  • Core setup remains consistent: ReactiveForms, FormArray logic

  • UI usage with ion-radio and ion-checkbox is the same

  • Form structure uses FormArray of FormControl and FormGroup No Angular code rewrite needed for modern versions

You can find the full source code on our GitHub.

We know that building beautifully designed Ionic apps from scratch can be frustrating and very time-consuming. Check Ionic 4 - Full Starter App and save development and design time. Android, iOS, and PWA, 100+ Screens and Components, the most complete and advanced Ionic Template.

That's just the basics. If you need more deep learning about Ionic, Angular, and TypeScript, you can take the following cheap course:

Thanks!