Ionic

Compose Ionic 5 emails with attachments (free source)

Share your learning

Let’s build the ionic 5 emails application where we can compose an email and we can also add attachment to this email. 

It will basically use native email application of your device when you click on the send email button.

We are going to use the cordova email-composer plugin to just compose the email with required input fields.

We will also use the capacitor camera API to add the attachment to our email. The capacitor camera API is very easy to use and I found it more useful than cordova camera plugin.

Live Demo

Create the ionic 5 email application

Run the following command to create your application or if you are using your existing project then skip this step.

ionic start ionic-email sidemenu --type=angular

Now, your basic project is ready.

Run the following commands to generate required pages and services.

ionic g page pages/email-composer
ionic g service services/file/file

Install the ionic Cordova email composer

For updated info about cli commands checkout official docs.

npm install cordova-plugin-email-composer
npm install @ionic-native/email-composer
ionic cap sync

It will help us to compose the email and open our native email application to send that email.

Install the Capacitor Camera API

You can checkout capacitor official docs for latest information.

npm install @capacitor/camera
npx cap sync

This API requires storage permissions while built for android. The androidManifest.xml will be found at the following path.

android/app/src/main/AndroidManifest.xml

If currently you don’t have this path then don’t worry it will automatically be added to your project, when you run the capacitor commands to build for android.

We need to paste the below permissions to this xml file.

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

Build an email compose form

Now, let’s start with our first page of the application.

We are not going to change our side menu, it is the same as provided by the ionic start template. Because it’s not the purpose of our tutorial.

We will simply use the folder.page.ts to demonstrate the inbox but you can modify it if you want. 

Our major focus is on email composer, so, we just have added some basic static stuff to this page and added a compose email button.

This button will navigate us to email composer form. That’s important. 

Open the create-email.module.ts and add the email-composer to the provider array.

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';

import { IonicModule } from '@ionic/angular';

import { CreateEmailPageRoutingModule } from './create-email-routing.module';

import { CreateEmailPage } from './create-email.page';
import { EmailComposer } from '@ionic-native/email-composer/ngx';

@NgModule({
  imports: [
    CommonModule,
    FormsModule,
    ReactiveFormsModule,
    IonicModule,
    CreateEmailPageRoutingModule,
  ],
  declarations: [CreateEmailPage],
  providers: [EmailComposer]
})
export class CreateEmailPageModule {}

Now, we have used the angular forms here to get the email related inputs, validate them. Then we are using an email composer to send the data to our native email application.

import { Component } from '@angular/core';
import { FormGroup, FormControl, Validators, FormBuilder } from '@angular/forms';
import { EmailComposer, EmailComposerOptions } from '@ionic-native/email-composer/ngx';
import { FileService } from 'src/app/services/file/file.service';

@Component({
  selector: 'app-create-email',
  templateUrl: './create-email.page.html',
  styleUrls: ['./create-email.page.scss'],
})
export class CreateEmailPage {

  showOtherTo: boolean = false;
  emailForm: FormGroup;
  attachments: any = [];

  formErrors: any = {};

  constructor(
    private emailComposer : EmailComposer,
    private formBuilder : FormBuilder,
    public fileService: FileService
  ) { 
    console.log(this.emailComposer);

    this.emailForm = this.formBuilder.group({
      to : new FormControl('', Validators.compose([ 
                              Validators.required, 
                              Validators.email, 
                              Validators.maxLength(50) 
                          ])),
      cc : new FormControl('', Validators.compose([ 
                                Validators.email, 
                                Validators.maxLength(50) 
                              ])),
      bcc : new FormControl('', Validators.compose([ 
                              Validators.email, 
                              Validators.maxLength(50) 
                            ])),
      subject : new FormControl('', Validators.compose([ 
                                      Validators.required, 
                                      Validators.maxLength(50) 
                                    ])),
      body : new FormControl('', Validators.compose([ 
                                  Validators.required, 
                                  Validators.maxLength(500) 
                                ])),
    })

  }

  sendEmail() {
    console.log(this.emailForm.value);

    if ( !this.emailForm.valid ) {
      console.error('Invalid form is not accepted!');
      return false;
    }

    let files = [];

    this.fileService.files.forEach(file => {
      files.push(file.path);      
    });

    let email: EmailComposerOptions = {
      ...this.emailForm.value,
      attachments : files,
      isHtml: true
    }

    // Send a text message using default options
    this.emailComposer.open(email);
  }

  selectFile() {
    this.fileService.selectImage();
  }

  deleteAttachment( i: number ) {
    console.info(`Delete attachment with index -> ${i}`);
    if ( this.fileService.files[i] ) {
      this.fileService.files.splice(i, 1);
    }
  }

  getError( input : string ) {
    const errors = this.emailForm.controls[input].errors || {};
    const messages = {
      required : `(${input}) field is required`,
      email : `(${input}) field should be a valid email`,
      maxlength : `(${input}) field should be less than `
    }

    for (let error of Object.keys(errors)) {
      
      if (messages[error]) {
        if ( error === 'maxlength' || error === 'minlength' ) {
          this.formErrors[input] = messages[error] + errors[error]['requiredLength'] + ' chars';
        } else {
          this.formErrors[input] = messages[error];
        }
      } else {
        console.error(`${error} - equivalent message not found!`);
      }
      
      console.log(errors);
    }

    if ( Object.keys(errors).length === 0 ) {
      this.formErrors[input] = null;
    }
  }

}

Add attachment to ionic 5 emails

We are using the capacitor camera API to add the attachment to our email. But we don’t need to add this camera to our module file and providers array. It is directly accessible in the application.

We are using a file service here, as we have already created it in the first step. So, just import the file service to the email-composer page.

import { Injectable, NgZone } from '@angular/core';
import { ActionSheetController, Platform } from '@ionic/angular';
import { Camera, CameraResultType, CameraSource } from '@capacitor/camera';

@Injectable({
  providedIn: 'root'
})
export class FileService {

  files: any = [];

  constructor(
    private actionSheetCtrl: ActionSheetController,
    private plt: Platform,
    private zone: NgZone
  ) { }

  async selectImage() {
    const actionSheet = await this.actionSheetCtrl.create({
        header: "Select Image source",
        buttons: [{
                text: 'Select from gallery',
                icon: !this.plt.is('ios') ? 'image' : null,
                handler: () => {
                    this.takePicture(CameraSource.Photos);
                }
            },
            {
                text: 'Take a pic',
                icon: !this.plt.is('ios') ? 'camera' : null,
                handler: () => {
                    this.takePicture(CameraSource.Camera);
                }
            },
            {
                text: 'Cancel',
                icon: !this.plt.is('ios') ? 'close' : null,
                role: 'cancel'
            }
        ]
    });
    await actionSheet.present();
  }

  async takePicture(sourceType: CameraSource) {

    const capturedPhoto = await Camera.getPhoto({
      resultType: CameraResultType.Uri,
      source: sourceType,
      quality: 60
    });

    this.zone.run(() => {
      this.files.unshift({
        path: capturedPhoto.path,
        src: capturedPhoto.webPath
      });
    })
  
    console.log(this.files);
  }

}

In the file service, we have imported the capacitor Camera, CameraResultType and CameraSource.

We are using an action-sheet-controller to get the user’s choice of file source like camera or gallery. Then, we are getting the file and updating our files property. 

But, we have to update our files property in the ngZone otherwise it won’t be able to update the view part of the email composer form.

We are adding the attached images in the card form and the user can remove them by using the remove button given on that card.

At the end when the user clicks on the send-Email button, it will add the attached files to the email-composer object and send them to native email application. 

That’s it. How to send emails using the ionic 5 application? Or ionic 5 emails application basic tutorial.

Hope you enjoy the tutorial. Source code link is given with the live demo screen.

See you in the next learning journey.

Satpal

Recent Posts

How to Switch PHP Versions in XAMPP Easily: Managing Multiple PHP Versions on Ubuntu

Today we are going to learn about managing multiple PHP versions on ubuntu with xampp.…

1 year ago

How to Use Coding to Improve Your Website’s SEO Ranking?

Let's understand about how to use coding to improve your website's SEO. In today’s computerized…

1 year ago

Most Important Linux Commands for Web Developers

Let's understand the most important linux commands for web developers. Linux, as an open-source and…

1 year ago

Top 75+ Laravel Interview Questions Asked by Top MNCs

Today we are going to discuss top 75+ Laravel interview questions asked by top MNCs.Laravel,…

1 year ago

Mailtrap Integration for Email Testing with Laravel 10

Today we will discuss about the Mailtrap integration with laravel 10 .Sending and receiving emails…

1 year ago

Firebase Cloud Messaging (FCM) with Ionic 6: Push Notifications

Today we are going to integrate FCM (Firebase Cloud Messaging) push notifications with ionic application.Firebase…

1 year ago