Ionic 4 and Angular 8: Radio Button and Checkbox in FormArray

by Didin J. on Aug 08, 2019 Ionic 4 and Angular 8: Radio Button and Checkbox in FormArray

An example of radio button and checkbox in FormArray using Ionic Framework 4, Angular 8, ReactiveFormsModule, and FormBuilder

Ionic 4 have the built-in radio button, group, and checkbox components. This time, we will show you how to use the radio button and checkbox in Angular 8 FormArray. Sometimes, we have an array of data that contains an array which an array contains multiple radio buttons or checkboxes. The Ionic 4 radio button or checkbox working smoothly with Angular FormBuilder. For example, a series of multiple-choice quiz or another example that required a series of a radio button or checkbox in an Angular FormGroup.

In this tutorial, we will implement this Ionic 4 radio buttons and checkboxes for the quiz or questionnaire mobile app. Some question has a single choice of an answer which represent by a series of radio button and text. The other question has multiple answers to choose using Ionic 4 checkboxes. The results will be displayed in the details page which has an edit button to edit that data. You also will learn how to use selected value for Ionic Radio Button and Checkbox.


The shortcut of the steps:


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

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

Before going to the main steps, we assume that you have to install Node.js. Next, upgrade or install new Ionic 4 CLI by open the terminal or Node command line then type this command.

sudo npm install -g ionic

You will get the latest Ionic 4 CLI in your terminal or command line. Check the version by type this command.

ionic -v
5.2.3

Right now, we are using the latest Ionic CLI version 5.2.3. To update the Angular CLI, type this command.

sudo npm install -g @angular/cli

Now, the Angular version should be like this.

ng version
Angular CLI: 8.2.0


1. Create a New Ionic 4 Angular 8 Application

We will be using Ionic CLI to create a new Ionic 4 application with the type of Angular. Type this command to create it.

ionic start ionic4-radio --type=angular

Choose the blank starter template for now and it will install the required NPM modules. Next, go to the newly created Ionic 4 project folder.

cd ./ionic4-radio

Check the installed Angular and CLI version and other information by typing this command.

ionic info

Here they are what we are using.

Ionic:

   Ionic CLI                     : 5.2.3 (/usr/local/lib/node_modules/ionic)
   Ionic Framework               : @ionic/angular 4.7.1
   @angular-devkit/build-angular : 0.801.3
   @angular-devkit/schematics    : 8.1.3
   @angular/cli                  : 8.1.3
   @ionic/angular-toolkit        : 2.0.0

Utility:

   cordova-res : not installed
   native-run  : 0.2.5 (update available: 0.2.8)

System:

   NodeJS : v10.15.1 (/usr/local/bin/node)
   npm    : 6.10.2
   OS     : macOS Mojave

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

ionic serve

If there's a question like below, just type `Y`.

Looks like @ionic/lab isn't installed in this project.

       This package is required for this command to work properly. The package provides a
       CLI utility, but the ionic-lab binary was not found in your PATH.

? Install @ionic/lab? (Y/n)

The Ionic application will open automatically in your default web browser.

Ionic 4 Radio Button Checkbox Angular FormArray FormGroup FormControl - Ionic Lab


2. Install and Configure the Required Ionic and Angular Modules

Actually, the required Ionic 4 and Angular 8 modules already installed when creating a new Ionic application with the type of Angular. All we do just make the required modules usable from the Ionic page. For that, open and edit `src/app/app.module.ts` then add or modify this import.

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

Add the ReactiveFormsModule to the `@NgModule` imports, so it will look like this.

imports: [
  BrowserModule,
  IonicModule.forRoot(),
  FormsModule,
  ReactiveFormsModule,
  AppRoutingModule
],

Next, we will register ReactiveFormsModule in each Ionic 4-page modules. Before doing that, we need to generate additional Ionic 4 page for details and edit. Type this command to generate the Ionic pages.

ionic -g page details
ionic -g page edit

We just added above two pages because to display a data form we will use existing Ionic Home Page. Next, to enable the ReactiveFormsModule in Ionic 4 template, open and edit `src/app/home/home.module.ts` then add or modify this import.

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

Add the ReactiveFormsModule to the `@NgModule` imports array.

imports: [
  CommonModule,
  FormsModule,
  ReactiveFormsModule,
  IonicModule,
  ...
],

Do the same way to `src/app/edit/edit.module.ts` file. It's not necessary for details module because it just contains text instead of Angular FormGroup.


3. Create an Ionic 4 Form for Questionnaire Form

To display a questionnaire form, we will use existing Ionic 4 Home page. Open and edit `src/app/home/home.page.ts` then add these required imports of Angular Form and Router.

import { FormBuilder, FormGroup, FormArray } from '@angular/forms';
import { Router  } from '@angular/router';

Declare a FormGroup variable inside the first line of the Ionic class.

qsForm: FormGroup;

Declare the variables for the questionnaire array of objects. We use a separate array of objects to make easier implement to different single and multiple selections that using Ionic 4 radio button and checkbox.

singleChoice: any[] = [
{
  nbr: '1',
  question: 'What is your job?',
  choice1: 'Full Stack Developer',
  choice2: 'Frontend Developer',
  choice3: 'Backend Developer',
  choice4: 'Database Administrator',
  choice: ''
},
{
  nbr: '2',
  question: 'Which frontend framework do you use?',
  choice1: 'Angular',
  choice2: 'React',
  choice3: 'Vue',
  choice4: 'Ember',
  choice: ''
},
{
  nbr: '3',
  question: 'Which backend framework do you use?',
  choice1: 'Spring',
  choice2: 'Django',
  choice3: 'Laravel',
  choice4: 'Ruby on Rails',
  choice: ''
}
];

multiChoice: any[] = [
{
  nbr: '4',
  question: 'Choose one or more of your frontend skills!',
  choice1: 'Angular',
  choice2: 'React',
  choice3: 'Vue',
  choice4: 'Ember',
  choice: ''
},
{
  nbr: '5',
  question: 'Choose one or more of your backend skills!',
  choice1: 'Spring',
  choice2: 'Django',
  choice3: 'Laravel',
  choice4: 'Ruby on Rails',
  choice: ''
}
];

You can use an array of objects from JSON response of the RESTAPI service as well. Next, inject FormBuilder and Router to the constructor and fill the constructor with the Angular FormGroup initialization using the Angular FormBuilder.

constructor(private formBuilder: FormBuilder, private router: Router) {
  this.qsForm = this.formBuilder.group({
    quests: this.formBuilder.array([
      this.initQuests()
    ]),
    quests2: this.formBuilder.array([
      this.initQuests()
    ])
  });
}

Inside that constructor FormBuilder array constructs by the function that builds a FormArray with their FormControl. So, add a function to construct that FormArray.

initQuests() {
  return this.formBuilder.group({
    nbr: '',
    question: '',
    choice1: '',
    choice2: '',
    choice3: '',
    choice4: '',
    choice: '',
  });
}

That Angular FormArray contains a group of key and value pairs which the nbr, question, choice1-4 will display to FormGroup and choice field as the result holder. Next, add a function to mapping or fill the data from the array variable to the FormGroup and FormArray.

setQuest(quests: any) {
  const arr = new FormArray([]);
  quests.forEach((q: any) => {
    arr.push(this.formBuilder.group({
      nbr: q.nbr,
      question: q.question,
      nchoice1br: q.choice1,
      choice1: q.choice1,
      choice2: q.choice2,
      choice3: q.choice3,
      choice4: q.choice4,
      choice: ''
    }));
  });
  return arr;
}

Call that function from Angular NgOnInit function.

ngOnInit() {
  this.qsForm = this.formBuilder.group({
    quests: this.setQuest(this.singleChoice),
    quests2: this.setQuest(this.multiChoice)
  });
}

The result from the multiple selections of the ion-checkbox will put to a single string variable that separated with commas. So, we will add a function that gets the selected ion-checkbox and adds/remove it to/from the string variable.

selectChecked(i: any, value: any) {
  const formArray = this.qsForm.controls.quests2 as FormArray;
  const formGroup = formArray.at(i) as FormGroup;
  const choice = formGroup.controls.choice;
  if (choice.value.search(value) === -1) {
    if (choice.value !== '') {
      choice.setValue(choice.value + ',' + value);
    } else {
      choice.setValue(value);
    }
  } else {
    const splitted = choice.value.split(',');
    const idx = splitted.indexOf(value, 0);
    splitted.splice(idx, 1);
    choice.setValue(splitted.toString());
  }
}

Next, add a function to collect result value from the Angular FormGroup and send it to details page as a JSON string.

onSubmit() {
  const submitData: any[] = [];
  this.qsForm.value.quests.forEach((qu: any) => {
    submitData.push({
      nbr: qu.nbr,
      question: qu.question,
      choice1: qu.choice1,
      choice2: qu.choice2,
      choice3: qu.choice3,
      choice4: qu.choice4,
      answer: qu.choice
    });
  });
  this.qsForm.value.quests2.forEach((qu2: any) => {
    submitData.push({
      nbr: qu2.nbr,
      question: qu2.question,
      choice1: qu2.choice1,
      choice2: qu2.choice2,
      choice3: qu2.choice3,
      choice4: qu2.choice4,
      answer: qu2.choice
    });
  });
  this.router.navigate(['/details/' + JSON.stringify(submitData)]);
}

Next, open and edit `src/app/home/home.page.html` then replace all HTML tags with these Angular FormGroup that contains Angular FormArray, FormControls, Ionic Radio Button, Checkbox, and Submit Button.

<ion-header>
  <ion-toolbar>
    <ion-title>
      Ionic Blank
    </ion-title>
  </ion-toolbar>
</ion-header>

<ion-content>
  <form [formGroup]="qsForm">
    <ion-list>
      <ion-list-header>
        <ion-label>Single Choice:</ion-label>
      </ion-list-header>
      <div formArrayName="quests">
        <div *ngFor="let qs of qsForm.get('quests')['controls']; let i=index" [formGroupName]="i">
          <ion-radio-group formControlName="choice">
            <ion-list-header>
              <ion-label>{{qs.controls.nbr.value}}. {{qs.controls.question.value}}</ion-label>
            </ion-list-header>
            <ion-item>
              <ion-label>{{qs.controls.choice1.value}}</ion-label>
              <ion-radio slot="start" [value]="qs.controls.choice1.value"></ion-radio>
            </ion-item>
            <ion-item>
              <ion-label>{{qs.controls.choice2.value}}</ion-label>
              <ion-radio slot="start" [value]="qs.controls.choice2.value"></ion-radio>
            </ion-item>
            <ion-item>
              <ion-label>{{qs.controls.choice3.value}}</ion-label>
              <ion-radio slot="start" [value]="qs.controls.choice3.value"></ion-radio>
            </ion-item>
            <ion-item>
              <ion-label>{{qs.controls.choice4.value}}</ion-label>
              <ion-radio slot="start" [value]="qs.controls.choice4.value"></ion-radio>
            </ion-item>
          </ion-radio-group>
        </div>
      </div>
      <ion-list-header>
        <ion-label>Multiple Choices:</ion-label>
      </ion-list-header>
      <div formArrayName="quests2">
        <div *ngFor="let qs2 of qsForm.get('quests2')['controls']; let j=index" [formGroupName]="j">
          <ion-radio-group>
            <ion-list-header>
              <ion-label>{{qs2.controls.nbr.value}}. {{qs2.controls.question.value}}</ion-label>
            </ion-list-header>
            <ion-item>
              <ion-label>{{qs2.controls.choice1.value}}</ion-label>
              <ion-checkbox slot="start" [value]="qs2.controls.choice1.value" (click)="selectChecked(j, qs2.controls.choice1.value)"></ion-checkbox>
            </ion-item>
            <ion-item>
              <ion-label>{{qs2.controls.choice2.value}}</ion-label>
              <ion-checkbox slot="start" [value]="qs2.controls.choice2.value" (click)="selectChecked(j, qs2.controls.choice2.value)"></ion-checkbox>
            </ion-item>
            <ion-item>
              <ion-label>{{qs2.controls.choice3.value}}</ion-label>
              <ion-checkbox slot="start" [value]="qs2.controls.choice3.value" (click)="selectChecked(j, qs2.controls.choice3.value)"></ion-checkbox>
            </ion-item>
            <ion-item>
              <ion-label>{{qs2.controls.choice4.value}}</ion-label>
              <ion-checkbox slot="start" [value]="qs2.controls.choice4.value" (click)="selectChecked(j, qs2.controls.choice4.value)"></ion-checkbox>
            </ion-item>
          </ion-radio-group>
        </div>
      </div>
      <ion-item>
        <ion-button shape="round" class="submit-button" (click)="onSubmit()">Submit</ion-button>
      </ion-item>
    </ion-list>
  </form>
</ion-content>

Don't forget to add a Global style for the button inside by open and edit `src/global.scss` then add these style class.

.submit-button {
    width: 100%;
    height: 45px;
    font-size: 20px;
    margin-top: 16px;
}


4. Display the Details in Ionic 4 Details Page

The JSON string data that send from the Home Page or Angular Form receive by the details page and converted to the Object or Array of objects. Open and edit `src/app/details/details.page.ts` then add this import of Angular Router and ActivatedRoute.

import { ActivatedRoute, Router  } from '@angular/router';

Declare a variable that represents an array of objects before the constructor.

data: any[] = [];

Inject the constructor with Angular Router and ActivatedRoute modules and fill the constructor with the data conversion between JSON string that catch from the params to the array of objects variable.

constructor(private activatedRoute: ActivatedRoute, private router: Router) {
  this.data = JSON.parse(this.activatedRoute.snapshot.params.data);
}

Add a function that navigates to Edit Page include the JSON string as a parameter.

edit(data: any[]) {
  this.router.navigate(['/edit/' + JSON.stringify(data)]);
}

Next, open and edit `src/app/details/details.page.html` then replace all HTML tags with these.

<ion-header>
  <ion-toolbar>
    <ion-buttons slot="start">
      <ion-back-button></ion-back-button>
    </ion-buttons>
    <ion-title>details</ion-title>
  </ion-toolbar>
</ion-header>

<ion-content>
  <ion-list>
    <ion-item *ngFor="let d of data" lines="full">
      <ion-label>
        <h3 text-wrap >{{d.nbr}}. {{d.question}}</h3>
        <p text-wrap >{{d.answer}}</p>
      </ion-label>
    </ion-item>
    <ion-item>
      <ion-button shape="round" class="submit-button" (click)="edit(data)">Edit</ion-button>
    </ion-item>
  </ion-list>
</ion-content>


5. Create an Edit Questionnaire Form in Ionic 4 Page

Inside details page, there's a button that will navigate to Edit questionnaire page. Open and edit `src/app/edit/edit.page.ts` then add these imports of Angular FormBuilder, FormGroup, FormArray, ActivatedRoute, and Router.

import { FormBuilder, FormGroup, FormArray } from '@angular/forms';
import { ActivatedRoute, Router  } from '@angular/router';

Declare the variables that hold Angular FormGroup and array of objects.

qsForm: FormGroup;
data: any[] = [];

Inject the constructor with above imported Angular modules and fill with data capture from the params that converted from JSON string to array of objects. Also, initialize Angular FormGroup using FormBuilder.

constructor(private formBuilder: FormBuilder, private router: Router, private activatedRoute: ActivatedRoute) {
  this.data = JSON.parse(this.activatedRoute.snapshot.params.data);
  this.qsForm = this.formBuilder.group({
    quests: this.formBuilder.array([
      this.initQuests()
    ]),
    quests2: this.formBuilder.array([
      this.initQuests()
    ])
  });
}

Add a function that called from FormGroup initialization to initialize FormArray and its FormControls.

initQuests() {
  return this.formBuilder.group({
    nbr: '',
    question: '',
    choice1: '',
    choice2: '',
    choice3: '',
    choice4: '',
    answer: '',
  });
}

Add a function for fill FormGroup, FormArray, and FormControls from the array of objects or data.

setQuest(quests: any) {
  const arr = new FormArray([]);
  quests.forEach((q: any) => {
    arr.push(this.formBuilder.group({
      nbr: q.nbr,
      question: q.question,
      nchoice1br: q.choice1,
      choice1: q.choice1,
      choice2: q.choice2,
      choice3: q.choice3,
      choice4: q.choice4,
      answer: q.answer
    }));
  });
  return arr;
}

Call that function from NgOnInit function.

ngOnInit() {
  this.qsForm = this.formBuilder.group({
    quests: this.setQuest(this.data.slice(0, 3)),
    quests2: this.setQuest(this.data.slice(3, 5))
  });
}

Add a function that will handle the changes of ion-checkbox selected value and keep it in a string FormControl.

selectChecked(i: any, value: any) {
  const formArray = this.qsForm.controls.quests2 as FormArray;
  const formGroup = formArray.at(i) as FormGroup;
  const choice = formGroup.controls.choice;
  if (choice.value.search(value) === -1) {
    if (choice.value !== '') {
      choice.setValue(choice.value + ',' + value);
    } else {
      choice.setValue(value);
    }
  } else {
    const splitted = choice.value.split(',');
    const idx = splitted.indexOf(value, 0);
    splitted.splice(idx, 1);
    choice.setValue(splitted.toString());
  }
}

Add a function for setting the default checked ion-checkbox by the data from params or RESTAPI service.

setChecked(i: any, value: any): boolean {
  const formArray = this.qsForm.controls.quests2 as FormArray;
  const formGroup = formArray.at(i) as FormGroup;
  const answer = formGroup.controls.answer;
  if (answer.value.search(value) === -1) {
    return false;
  } else {
    return true;
  }
}

Add a function to collection the updated data from the Angular FormGroup, FormArray, and FormControl as an array of objects then send back to the details page as JSON string.

onSubmit() {
  const submitData: any[] = [];
  this.qsForm.value.quests.forEach((qu: any) => {
    submitData.push({
      nbr: qu.nbr,
      question: qu.question,
      answer: qu.choice
    });
  });
  this.qsForm.value.quests2.forEach((qu2: any) => {
    submitData.push({
      nbr: qu2.nbr,
      question: qu2.question,
      answer: qu2.choice
    });
  });
  this.router.navigate(['/details/' + JSON.stringify(submitData)]);
}

Next, open and edit `src/app/edit/edit.page.html` then replace all HTML tags with these tags that contain Angular FormGroup, FormArray, FormControl, Ionic 4 Radio Button, Checkbox, and Submit Button.

<ion-header>
  <ion-toolbar>
    <ion-buttons slot="start">
      <ion-back-button></ion-back-button>
    </ion-buttons>
    <ion-title>edit</ion-title>
  </ion-toolbar>
</ion-header>

<ion-content>
  <form [formGroup]="qsForm">
    <ion-list>
      <ion-list-header>
        <ion-label>Single Choice:</ion-label>
      </ion-list-header>
      <div formArrayName="quests">
        <div *ngFor="let qs of qsForm.get('quests')['controls']; let i=index" [formGroupName]="i">
          <ion-radio-group formControlName="answer">
            <ion-list-header>
              <ion-label>{{qs.controls.nbr.value}}. {{qs.controls.question.value}}</ion-label>
            </ion-list-header>
            <ion-item>
              <ion-label>{{qs.controls.choice1.value}}</ion-label>
              <ion-radio slot="start" [value]="qs.controls.choice1.value"></ion-radio>
            </ion-item>
            <ion-item>
              <ion-label>{{qs.controls.choice2.value}}</ion-label>
              <ion-radio slot="start" [value]="qs.controls.choice2.value"></ion-radio>
            </ion-item>
            <ion-item>
              <ion-label>{{qs.controls.choice3.value}}</ion-label>
              <ion-radio slot="start" [value]="qs.controls.choice3.value"></ion-radio>
            </ion-item>
            <ion-item>
              <ion-label>{{qs.controls.choice4.value}}</ion-label>
              <ion-radio slot="start" [value]="qs.controls.choice4.value"></ion-radio>
            </ion-item>
          </ion-radio-group>
        </div>
      </div>
      <ion-list-header>
        <ion-label>Multiple Choices:</ion-label>
      </ion-list-header>
      <div formArrayName="quests2">
        <div *ngFor="let qs2 of qsForm.get('quests2')['controls']; let j=index" [formGroupName]="j">
          <ion-radio-group>
            <ion-list-header>
              <ion-label>{{qs2.controls.nbr.value}}. {{qs2.controls.question.value}}</ion-label>
            </ion-list-header>
            <ion-item>
              <ion-label>{{qs2.controls.choice1.value}}</ion-label>
              <ion-checkbox slot="start" [value]="qs2.controls.choice1.value" [checked]="setChecked(j, qs2.controls.choice1.value)" (click)="selectChecked(j, qs2.controls.choice1.value)"></ion-checkbox>
            </ion-item>
            <ion-item>
              <ion-label>{{qs2.controls.choice2.value}}</ion-label>
              <ion-checkbox slot="start" [value]="qs2.controls.choice2.value" [checked]="setChecked(j, qs2.controls.choice2.value)" (click)="selectChecked(j, qs2.controls.choice2.value)"></ion-checkbox>
            </ion-item>
            <ion-item>
              <ion-label>{{qs2.controls.choice3.value}}</ion-label>
              <ion-checkbox slot="start" [value]="qs2.controls.choice3.value" [checked]="setChecked(j, qs2.controls.choice3.value)" (click)="selectChecked(j, qs2.controls.choice3.value)"></ion-checkbox>
            </ion-item>
            <ion-item>
              <ion-label>{{qs2.controls.choice4.value}}</ion-label>
              <ion-checkbox slot="start" [value]="qs2.controls.choice4.value" [checked]="setChecked(j, qs2.controls.choice4.value)" (click)="selectChecked(j, qs2.controls.choice4.value)"></ion-checkbox>
            </ion-item>
          </ion-radio-group>
        </div>
      </div>
      <ion-item>
        <ion-button shape="round" class="submit-button" (click)="onSubmit()">Submit</ion-button>
      </ion-item>
    </ion-list>
  </form>
</ion-content>


6. Run and Test the Ionic 4, Angular 8 Application

As usual, an Ionic 4 with Angular type application run in the browser using this command.

ionic serve -l

And here's the complete Ionic 4 and Angular 8 application looks like.

Ionic Angular Radio Button Checkbox FormGroup FormArray FormControl - home page
Ionic Angular Radio Button Checkbox FormArray - Detail Page
Ionic Angular Radio Button Checkbox FormArray - Edit Page

That it's, the Ionic 4 Radio Button and Checkbox inside Angular 8 FormArray. You can find the full source code from 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 advance Ionic Template.

That just the basic. If you need more deep learning about Ionic, Angular, and Typescript, you can take the following cheap course:

Thanks!

Loading…