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
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.
Summary
-
CLI & versions updated: Ionic 8 + Angular 20
-
Core setup remains consistent: ReactiveForms, FormArray logic
-
UI usage with
ion-radio
andion-checkbox
is the same -
Form structure uses
FormArray
ofFormControl
andFormGroup
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:
- IONIC 4 Design Hybrid Mobile Applications IOS & Android
- WordPress Rest API and Ionic 4 (Angular) App With Auth
- Mobile App from Development to Deployment - IONIC 4
- Ionic 4 Crash Course with Heartstone API & Angular
- Ionic 4 Mega Course: Build 10 Real World Apps
Thanks!