Adding email functionality to an Ionic application

October 9, 2017, 7:27 pm Categories:

Categories

Email might be, in one form or another, over 50 years old but its continued popularity as a means of communication is a reliable indicator that, at some point, we might need to integrate such functionality into our Ionic applications.

There's a couple of ways we could accomplish this:

  1. Build an angular form and submit the data to a remote script (using PHP for example) which would then mail that to the designated recipient
  2. Install the Ionic Native Email Composer plugin and use this to create and pre-populate a new message in our device's e-mail client software

For the purposes of this tutorial we're going to explore the second approach and build a very basic application using the Ionic Native Email Composer plugin.

This will allow us to create an e-mail message, attach an image (using the Ionic Native Camera plugin) and then open that within the device's default e-mail application (in the example shown in this tutorial that will be iOS's Apple Mail software) where it can then be mailed to the intended recipient.

Here's what we'll be building:

E-mail application demonstrating blank form, completed form and that form data pre-populating a form in the Apple Mail client on iOS

Laying the foundations

With the Ionic CLI (and this tutorial assumes you are using the latest version of Ionic and that your system is fully and properly configured for application development with the Ionic Framework) create the project for this tutorial with the following command:

ionic start ionic-mail blank

Once the project has been created run the following commands to:

  • Change into the project root directory
  • Add the necessary platforms
  • Install the Email Composer and Camera plugins/packages
  • Generate providers for handling e-mail composing and image selection
cd ./ionic-mail/
ionic cordova platform add ios
ionic cordova platform add android
ionic cordova plugin add cordova-plugin-email-composer
npm install --save @ionic-native/email-composer
ionic cordova plugin add cordova-plugin-camera
npm install --save @ionic-native/camera
ionic g provider email
ionic g provider image

After these commands have been executed open the project's root module: ionic-mail/src/app/app.module.ts and ensure that the configuration matches the following example (I.e. that the necessary providers and plugins are imported and declared where necessary):

import { BrowserModule } from '@angular/platform-browser';
import { ErrorHandler, NgModule } from '@angular/core';
import { IonicApp, IonicErrorHandler, IonicModule } from 'ionic-angular';
import { SplashScreen } from '@ionic-native/splash-screen';
import { StatusBar } from '@ionic-native/status-bar';
import { Camera } from '@ionic-native/camera';
import { EmailComposer } from '@ionic-native/email-composer';

import { MyApp } from './app.component';
import { HomePage } from '../pages/home/home';
import { EmailProvider } from '../providers/email/email';
import { ImageProvider } from '../providers/image/image';

@NgModule({
  declarations: [
    MyApp,
    HomePage
  ],
  imports: [
    BrowserModule,
    IonicModule.forRoot(MyApp)
  ],
  bootstrap: [IonicApp],
  entryComponents: [
    MyApp,
    HomePage
  ],
  providers: [
    StatusBar,
    SplashScreen,
    Camera,
    EmailComposer,
    {provide: ErrorHandler, useClass: IonicErrorHandler},
    EmailProvider,
    ImageProvider
  ]
})
export class AppModule {}

Once these have been completed we can start coding!

Let's begin with the image provider...

Handling attachments

As we will offer the ability to add attachments to our e-mails we need to be able to retrieve the media that will be used for this purpose.

In the context of this tutorial I have decided to only be able to add attachments in the form of images (for other media types you would need to look into using the Ionic Native File plugin).

To retrieve images for use as attachments we'll add the necessary logic to the recently created ImageProvider service to utilise the Ionic Native Camera plugin methods to access our device photolibrary and select images from there as and where required.

Open the ionic-mail/src/providers/image/image.ts service and add the following code (remove ALL references to the default Http service that is automatically added when a service is generated using the Ionic CLI):

import { Injectable } from '@angular/core';
import { Camera, CameraOptions } from '@ionic-native/camera';


@Injectable()
export class ImageProvider {


   constructor(private _CAMERA : Camera) 
   { }




  /**
   *
   * @public
   * @method selectPhotograph
   * @return {Promise}
   */
   selectPhotograph() : Promise<any>
   {
      return new Promise((resolve) =>
      {
         let cameraOptions : CameraOptions = {
             sourceType         : this._CAMERA.PictureSourceType.PHOTOLIBRARY,
             destinationType    : this._CAMERA.DestinationType.FILE_URI,
	         quality            : 100,
	         targetWidth        : 320,
	         targetHeight       : 240,
	         encodingType       : this._CAMERA.EncodingType.JPEG,
	         correctOrientation : true
         };

         this._CAMERA.getPicture(cameraOptions)
         .then((data : any) =>
         {            
            resolve(data);
         });

      });
   }

}

As you can see this is a very simple service and allows us to access the device photolibrary, select our image of choice and return that as a native File URI (which allows us to avoid common memory problems associated with returning the data as a base64 encoded data URI).

Given that this is all we need for the ImageProvider service let's now turn our attention to adding the necessary logic for composing e-mails to the EmailProvider service...

Composing e-mails

Within the ionic-mail/src/providers/email/email.ts service add the following code (once again remove ALL references to the default Http service that is automatically added when a service is generated using the Ionic CLI):

import { Injectable } from '@angular/core';
import { EmailComposer } from '@ionic-native/email-composer';


@Injectable()
export class EmailProvider {

   constructor(private _EMAIL   : EmailComposer) 
   { }




   /**
    *
    * @public
    * @method sendMail
    * @param to    			{string}    The primary e-mail address
    * @param cc    			{string}    The carbon copy e-mail address
    * @param bcc   			{string}    The blank carbon copy e-mail address
    * @param attachment     {string}    The attachment to be sent
    * @param subject        {string}    The subject for the e-mail message
    * @param body           {string}    The message content
    *
    */
   sendEmail(to         : string, 
             cc         : string, 
             bcc        : string, 
             attachment : string,
             subject    : string,
             body       : string) : void
   {
      // Use the plugin isAvailable method to check whether
      // the user has configured an email account
      this._EMAIL.isAvailable()
      .then((available: boolean) =>
      {

         // Check that plugin has been granted access permissions to 
         // user's e-mail account
         this._EMAIL.hasPermission()
         .then((isPermitted : boolean) =>
         {

            // Define an object containing the 
            // keys/values for populating the device 
            // default mail fields when a new message 
            // is created
            let email : any = {
               app 			: 'mailto',
               to 			: to,
               cc 			: cc,
               bcc 			: bcc,
               attachments 	: [
                 attachment
               ],
               subject 		: subject,
               body 		: body
            };

            // Open the device e-mail client and create 
            // a new e-mail message populated with the 
            // object containing our message data
            this._EMAIL.open(email);
         })
         .catch((error : any) =>
         {
            console.log('No access permission granted');
            console.dir(error);
         });
      })
      .catch((error : any) =>
      {
         console.log('User does not appear to have device e-mail account');
         console.dir(error);
      });
   }

}

This should be fairly straightforward to understand from the code and accompanying comments.

We simply supply our message data to the solitary sendEmail method, create a message object and make use of the following Email Composer plugin methods:

  • isAvailable
  • hasPermission
  • open

If the plugin returns no errors (permission related or otherwise) then we simply call the open method to - surprise, surprise - open the device default e-mail client and create a new message with fields pre-populated with data from the sendEmail method.

There is one HUGE gotcha though.

We cannot detect whether the user has sent or cancelled the e-mail once the default mail client opens on the user's device.

Bear this in mind when using the plugin as you'll probably want to add some extra logic/functionality to 'handle' that scenario.

Putting it together

Now that we have our services configured we need to 'plug' these into the application's HomePage component so that we can start creating draft e-mail messages which can then be opened within the device's default mail application.

Let's start with crafting the logic for the component class.

Open the ionic-mail/src/pages/home/home.ts file and add the following code:

import { Component } from '@angular/core';
import { AlertController, NavController } from 'ionic-angular';
import { FormGroup, FormControl, Validators, FormBuilder } from '@angular/forms';
import { ImageProvider } from '../../providers/image/image';
import { EmailProvider } from '../../providers/email/email';

@Component({
  selector: 'page-home',
  templateUrl: 'home.html'
})
export class HomePage {



   /**
    * @public
    * Property to assign a FormGroup object to
    */
   public form  : FormGroup;




   /**
    * @private
    * Property to assign an image file reference to
    */
   private _attachment : any;



   constructor(public navCtrl 		: NavController,
               private _ALERT       : AlertController,
               private _FORM	    : FormBuilder,
  		       private _IMAGE 		: ImageProvider,
  		       private _EMAIL       : EmailProvider) 
   {
      // Create a FormGroup object to implement validation 
      // on the template fields
      // VERY basic validation as you can see - I.e. NO empty fields!
      this.form = this._FORM.group({
         "to"            : ["", Validators.required],
         "cc"            : ["", Validators.required],
         "bcc"           : ["", Validators.required],
         "subject"       : ["", Validators.required],
         "message"       : ["", Validators.required]
      });
   }




   /**
    *
    * @public
    * @method retrieveAttachment
    * @return {none}
    */
   retrieveAttachment() : void
   {
      this._IMAGE.selectPhotograph()
      .then((attachment : any) => 
      {
         // Assign retrieved image to private property
         // which we'll subsequently access within the 
         // sendMessage method
         this._attachment = attachment;
      });
   }




   /**
    *
    * @public
    * @method displayMessage
    * @param title    	{string}      Heading for the alert window
    * @param subTitle   {string}      Message for the alert window
    * @return {none}
    */
   displayMessage(title : string, subTitle : string) : void
   {
      let alert : any 		=  this._ALERT.create({
         title 		: title,
         subTitle 	: subTitle,
         buttons    : ['Got it']
      });
      alert.present();
   }




   /**
    *
    * @public
    * @method sendMessage
    * @return {none}
    */
   sendMessage() : void
   {
      // Retrieve the validated form fields
      let to 		: string		= this.form.controls["to"].value,
          cc 		: string		= this.form.controls["cc"].value,
          bcc 		: string		= this.form.controls["cc"].value,          
          subject 	: string		= this.form.controls["subject"].value,    
          message 	: string		= this.form.controls["message"].value;

      // Has the user selected an attachment?
      if(this._attachment.length > 1)
      {
         // If so call the sendEmail method of the EmailProvider service, pass in
         // the retrieved form data and watch the magic happen! :)
         this._EMAIL.sendEmail(to, cc, bcc, this._attachment, subject, message);
      }
      else 
      {
         // Inform the user that they need to add an attachment
         this.displayMessage('Error', 'You need to select an attachment');
      }
   }

}

Pretty straightforward right?

Let's now add the necessary templating to complete our application.

Within the ionic-mail/src/pages/home/home.html template add the following HTML:

<ion-header>
   <ion-navbar>
      <ion-title>
         E-mailer
      </ion-title>
   </ion-navbar>
</ion-header>

<ion-content padding>
   
   <form 
      [formGroup]="form" 
      (ngSubmit)="sendMessage()">

      <ion-list>


         <ion-item margin-bottom> 
            <ion-label>To:</ion-label> 
            <ion-input
               type="email"
               formControlName="to"></ion-input> 
         </ion-item>


         <ion-item margin-bottom> 
            <ion-label>Cc:</ion-label> 
            <ion-input
               type="email"
               formControlName="cc"></ion-input> 
         </ion-item>


         <ion-item margin-bottom> 
            <ion-label>Bcc:</ion-label> 
            <ion-input
               type="email"
               formControlName="bcc"></ion-input> 
         </ion-item>


         <ion-item margin-bottom> 
            <span
               ion-button
               color="primary"
               text-center
               block 
               (click)="retrieveAttachment()">Attachment</span> 
         </ion-item>


         <ion-item margin-bottom> 
            <ion-label>Subject:</ion-label> 
            <ion-input
               type="string"
               formControlName="subject"></ion-input> 
         </ion-item>


         <ion-item margin-bottom> 
            <ion-label>Message:</ion-label> 
            <ion-textarea
               formControlName="message"></ion-textarea> 
         </ion-item>
   
   
         <button
            ion-button
            color="primary"
            text-center
            block [disabled]="!form.valid">Send your message</button>
      </ion-list> 
   </form>
</ion-content>

This should be fairly self-explanatory but I'll quickly break it down.

We add a form to the page which consists of the following fields:

  • To
  • Cc
  • Bcc
  • Subject
  • Message
  • And a <span> element with a retrieveAttachment() method assigned to its click event handler function

The form can only be submitted once the FormGroup validation has been successfully passed (as determined by the formControlName attributes on each field) and, that being the case, the sendMessage() method then handles the submitted data.

Nothing to it right?

Okay, with the logic and templating in place all that remains is to now build and run the application.

This assumes you will be using iOS (substitute with Android if building for that particular platform) and have a developer account configured (with the necessary certificates/profiles assigned to the application - my book Mastering Ionic : The Definitive Guide covers everything you need to know about configuring your application for device testing and submission to the Apple App Store and Google Play Store).

Connect your handheld device to your machine and execute the following commands within the Ionic CLI:

ionic build ios --prod
ionic run ios

All things being well you should be able to build the application, deploy that to your device, run the code and subsequently test like so:

E-mail application demonstrating blank form, completed form and that form data pre-populating a form in the Apple Mail client on iOS

In closing

The Ionic Native Email Composer plugin allows developers to create new e-mail messages and pre-populate these with data in the user's default e-mail software on their device.

As stated it does have one MAJOR limitation: there is no way (at least none that I have been able to discern from the documentation) to detect whether the e-mail has been sent or cancelled - it simply opens the default mail software.

This is something you will need to 'handle' should you choose to use this plugin in your Ionic applications but have fun experimenting with this and be sure to read the docs for further information.

Please feel free to share your thoughts and feedback with regards to this article using the commenting system below.

If you enjoyed what you've read here then please sign up to my mailing list and, if you haven't done so already, take a look at my e-book: Mastering Ionic for information about working with forms and components in Ionic (doesn't cover Firebase).

Tags

Categories

Post a comment

All comments are welcome and the rules are simple - be nice and do NOT engage in trolling, spamming, abusiveness or illegal behaviour. If you fail to observe these rules you will be permanently banned from being able to comment.

Top