Creating ToDo Application Using Ionic 2 CLI

Now, it is the time of Ionic 2 (Cross-platform mobile apps using AngularJS and TypeScript). If you know about AngularJS and TypeScript, this is your “cup of tea.”

In this tutorial, I am going to cover the following topics.

  • HTTP Services (Providers)
  • Fetching JSON Data From URL
  • Add task
  • Conditionally applying CSS class
  • Delete Task
  • Update Task & more..

You can find the code here also. So, feel free to play with the code yourself. In this tutorial, I am using Ionic 2 CLI (Command Line Interface) to have work convenience with Ionic 2. In other words using CLI will create a boilerplate ready for us. For setup and all other instructions, please go through the previous article here.

In this tutorial, we are developing a to-do application to simply list all the tasks, add tasks, delete tasks, and update tasks. I have already created the REST API using NodeJS, which will fetch data from MySQL. My Task table contains 3 columns - Id, Title, and Status.

Creating Data Structure/Class 

  1. [code language=”typescript”]  
  2. export class Task {  
  3.     constructor(public IdString,public TitleString,public StatusString){}  
  4. }  
  5. [/code]   

First, I have created a class and named it as Task. It will be used to store the JSON data.

What are Services?

We can say, Services mean - don’t repeat yourself (Try)!

Now, what does this mean?

Let’s say for example, we require one function which can be used by more than one components. Then, what happens that we just need to write the same code again and again for each component. When we want to change some logic in function, we need to change it on each and every component.

Or, instead of doing this, we simply create a Service in which we can write the code once and use it for as many components as we want, by simply injecting the Service Instance. In other language, Services keep our function and logic centralized.

In general, our service can perform the following tasks.

  • Communicate with database.
  • Communicate with components /classes.
  • Some other business logic which is accessible from various places of our application.

Creating Provider/Service

cmd> ionic g provider dbtaskservice 

  1. [code language=”typescript”]  
  2. public urlstring="https//localhost3000/Tasks/";   
  3. getAllTasks(){   
  4. return this._http.get(this.url)  
  5.   .map((responseResponse)=>response.json());  
  6.  }  
  7. [/code]   

The above function will return all the tasks from the database. But before creating this function, create the object of HTTP as shown below and also import rxjs/Rx for map and observable. 

  1. [code language=”typescript”]  
  2. import { Injectable } from '@angular/core';  
  3. import { Task } from '../pages/tasks/task';  
  4. import { Http,Response,RequestOptions,Headers } from '@angular/http';  
  5. import { Observable } from "rxjs/Observable";  
  6. import  'rxjs/Rx';  
  7.   
  8.   
  9.   
  10. @Injectable()  
  11. export class Dbtaskservice {  
  12. private allTaskTask[]=[];  
  13. private urlstring="https//localhost3000/Tasks/";  
  14.   constructor(public _http Http)   
  15.   {  
  16.     console.log('Hello Dbtaskservice Provider');  
  17.   }  
  18.   getAllTask()  
  19.  {  
  20.   return this._http.get(this.url)  
  21.   .map((responseResponse)=>response.json());  
  22.     
  23.   }  
  24.   deleteTask(itemTask){          
  25.      let headers = new Headers({ 'Content-Type' 'application/json' });  
  26.      let options = new RequestOptions({ headers headers });  
  27.      return this._http.delete(this.url+item.Id,  
  28.                       options)  
  29.                     .map((responseResponse)=>response.json());     
  30.   }  
  31.   addTask(itemTask){  
  32.        let body = JSON.stringify(item);  
  33.        let headers = new Headers({ 'Content-Type' 'application/json' });  
  34.        let options = new RequestOptions({ headers headers });  
  35.        return this._http.post(this.url,  
  36.                      body, options)  
  37.                     .map((responseResponse)=>response.json());  
  38.   }  
  39.   getTaskId(idany){  
  40.     return this._http.get(this.url+id)  
  41.   .map((responseResponse)=>response.json());  
  42.   }  
  43.   editTask(itemTask){  
  44.        let body = JSON.stringify(item);  
  45.        let headers = new Headers({ 'Content-Type' 'application/json' });  
  46.        let options = new RequestOptions({ headers headers });  
  47.        return this._http.put(this.url+item.Id,  
  48.                      body, options)  
  49.                     .map((responseResponse)=>response.json());  
  50.   }   
  51. }  
  52. [/code]   

So, here in the above service, I have created all methods for tasks like getAllTask(), addTask(), deleteTask() etc.

Note -

To make this service available to each component, it must be declared inside the provider array of app.module.ts (i.e. global declaration file), as shown below. 

  1. [code language=”typescript”]  
  2. import { NgModule } from '@angular/core';  
  3. import { IonicApp, IonicModule } from 'ionic-angular';  
  4. import { MyApp } from './app.component';  
  5. import { AboutPage } from '../pages/about/about';  
  6. import { ContactPage } from '../pages/contact/contact';  
  7. import { Dbtaskservice } from '../providers/dbtaskservice';  
  8. @NgModule({  
  9.   declarations: [  
  10.     MyApp,  
  11.     AboutPage,  
  12.     ContactPage  
  13. ],  
  14. providers [Dbtaskservice]  
  15. })  
  16. [/code]   

Creating Component

So far, I have created class and service, Now, it is time for component to display all the tasks.

cmd>ionic g page tasks

It will generate tasks directory inside pages directory.

Ionic 2 comes with global declaration concept. So, whenever creating a component, it must declare it in app.module.ts, inside the declaration array and entryComponents array as shown below. Then only can it be used. 

  1. [code language=”typescript”]  
  2. import { NgModule } from '@angular/core';  
  3. import { IonicApp, IonicModule } from 'ionic-angular';  
  4. import { MyApp } from './app.component';  
  5. import { AboutPage } from '../pages/about/about';  
  6. import { ContactPage } from '../pages/contact/contact';  
  7. import { TasksPage } from '../pages/tasks/tasks';  
  8. @NgModule({  
  9.   declarations [  
  10.    MyApp,  
  11.    AboutPage,  
  12.   ContactPage,  
  13.   TasksPage  
  14. ],  
  15. entryComponents [  
  16.     MyApp,  
  17.     AboutPage,  
  18.     ContactPage,  
  19.     HomePage,  
  20.     TasksPage  
  21.   ],  
  22. })  
  23. [/code]   

Component can be divided in two parts.

  1. HTML
  2. TypeScript

I will first start with the TypeScript part. The TypeScript can be further divided in 3 parts.

  1. Import section
  2. Component metadata
  3. Class

So, in our example,

  • First, create the array and name it as allTasks which is the type of task (which was created earlier).
  • Then, inject the dbtaskservice inside the constructor and create the instance of your service.
  • And then finally, call getAllTask method of your Service inside the ionViewDidLoad event. 
  1. [code language=”typescript”]  
  2. import { Component } from '@angular/core';  
  3. import { NavController ,LoadingController ,ToastController } from 'ionic-angular';  
  4. import { Dbtaskservice } from '../../providers/dbtaskservice';  
  5. import { Task } from '. /task';  
  6. @Component({  
  7.   selector 'page-tasks',  
  8.   templateUrl 'tasks.html'  
  9. })  
  10. export class Tasks {  
  11. allTaskTask[]=[];  
  12. titlestring;  
  13. idstring;  
  14.   constructor(public navCtrl NavController,public loadincontrollerLoadingController,public _dbtaskserviceDbtaskservice,public _toastToastController){  
  15.   }  
  16.   ionViewDidLoad() {  
  17.     let loadingdata=this.loadincontroller.create({  
  18.       content"Loading Tasks..."  
  19.     });  
  20.     loadingdata.present();  
  21.     this._dbtaskservice.getAllTask().subscribe(  
  22.       (dataTask[])=>{  
  23.         this.allTask=data;  
  24.       },  
  25.       function (error){  
  26.         console.log("error"+error)  
  27.       },  
  28.       function(){  
  29.         loadingdata.dismiss();  
  30.       }  
  31.     );  
  32.   }  
  33. }  
  34. [/code]   

In the above code, I have imported loadingcontroller from ionic-angular package, to show the progress bar on loading. I also imported toastcontroller to display the message on successfully inserting, deleting, or updating the task. Now, in HTML, I will loop through the allTask array and display all the tasks. So, the HTML will look like the following. 

  1. [code language=”html”]  
  2. <ion-header>  
  3.   <ion-navbar>  
  4.     <ion-title>taskdb</ion-title>  
  5.   </ion-navbar>  
  6. </ion-header>  
  7. <ion-content padding>  
  8. <ion-list inset>  
  9.   <ion-input placeholder="id" autofocus="" [(ngModel)]="id" ></ion-input>  
  10.   <ion-input placeholder="What needs to be done?"  [(ngModel)]="title" (keyup.enter)="addTask()" ></ion-input>  
  11.   <ion-item *ngFor="let t of allTask" ><!--(click)="taskSelected(t)"-->  
  12.        
  13.     <ion-label [ngClass]="{'donestatus' t.Status=='done','pendingstatus't.Status=='pending'}" >{{t.Title}}</ion-label>  
  14.     <ion-icon item-right (click)="deleteTask(t)"  ios="ios-trash" md="md-trash"></ion-icon>  
  15.     <ion-icon item-right (click)="updateTask(t)"  ios="ios-color-wand" md="md-color-wand"></ion-icon>  
  16.      <!--<ion-icon name="trash" ios="ios-trash" md="md-trash"></ion-icon>-->  
  17.   </ion-item>  
  18. </ion-list>  
  19. </ion-content>  
  20. [/code]   

Here, in the above HTML, I am also applying the class conditionally i.e. the completed task should be displayed in blue and uncompleted task should be displayed in red. 

  1. [code language=”css”]  
  2. page-taskdb {  
  3. .donestatus{  
  4. color blue;  
  5. }  
  6. .pendingstatus{  
  7.     color red;  
  8. }  
  9. }  
  10. [/code]   

Also, I have used two-way binding for adding the tasks. 

  1. [code language=”html”]  
  2. <ion-input placeholder="id" autofocus="" [(ngModel)]="id" ></ion-input>  
  3.   <ion-input placeholder="What needs to be done?"  [(ngModel)]="title" (keyup.enter)="addTask()" ></ion-input>  
  4. [/code]   

Two-way binding can be achieved using ngModel. Here, I have created the keyup event which gets fired on pressing the Enter key. The complete TypeScript will look like the following, after adding addTask, updateTask, and deleteTask. 

  1. [code language=”typescript”]  
  2. import { Component } from '@angular/core';  
  3. import { NavController ,LoadingController ,ToastController } from 'ionic-angular';  
  4. import { Dbtaskservice } from '../../providers/dbtaskservice';  
  5. import { Task } from './task';  
  6. @Component({  
  7.   selector 'page-tasks',  
  8.   templateUrl 'tasks.html'  
  9. })  
  10. export class Tasks {  
  11. allTaskTask[]=[];  
  12. titlestring;  
  13. idstring;  
  14.   constructor(public navCtrl NavController,public loadincontrollerLoadingController,public _dbtaskserviceDbtaskservice  
  15.   ,public _toastToastController){  
  16.   
  17.   }  
  18.   
  19.   ionViewDidLoad() {  
  20.     console.log('Hello Taskdb Page');  
  21.     let loadingdata=this.loadincontroller.create({  
  22.       content"Loading Tasks..."  
  23.     });  
  24.     loadingdata.present();  
  25.     this._dbtaskservice.getAllTask().subscribe(  
  26.       (dataTask[])=>{  
  27.         this.allTask=data;  
  28.         console.log(data);  
  29.       },  
  30.       function (error){  
  31.         console.log("error"+error)  
  32.       },  
  33.       function(){  
  34.         console.log("subscription done")  
  35.         loadingdata.dismiss();  
  36.       }  
  37.     );  
  38.   }  
  39. addTask(){  
  40.   let loadingdata=this.loadincontroller.create({  
  41.       content"Posting Tasks..."  
  42.     });  
  43.     loadingdata.present();  
  44. this._dbtaskservice.addTask(new Task(this.id,this.title,'pending'))  
  45. .subscribe(  
  46.   (data:Task)=>{  
  47.     if(data!=null){  
  48.       this.allTask.push(new Task(this.id,this.title,'pending'));  
  49.       this.title='';  
  50.       this.id='';  
  51.     }  
  52.   },  
  53.   function(error){},  
  54.   function(){  
  55.     loadingdata.dismiss();  
  56.   }  
  57.   
  58. );  
  59. }  
  60. updateTask(tTask){  
  61.   if(t.Status=='done')  
  62.   {  
  63.     t.Status='pending';  
  64.   }  
  65.   else  
  66.   {  
  67.     t.Status='done'  
  68.   }  
  69.   this._dbtaskservice.editTask(t).subscribe(  
  70.     (dataany)=>{  
  71.   
  72.       if(data.affectedRows==1)  
  73.       {  
  74.         let mes=this._toast.create({  
  75.         message'Task Updated Successfully',  
  76.         duration2000,  
  77.         position'bottom'  
  78.         });  
  79.           
  80.         mes.present();  
  81.       }  
  82.       else  
  83.       {  
  84.         let mes=this._toast.create({  
  85.         message'Error in Updating',  
  86.         duration2000,  
  87.         position'bottom'  
  88.         });  
  89.           
  90.         mes.present();  
  91.       }  
  92.     }  
  93.   );  
  94. }  
  95. deleteTask(tTask){  
  96.     
  97.   this._dbtaskservice.deleteTask(t).subscribe(  
  98.     (dataany)=>{  
  99.   
  100.       if(data.affectedRows==1){  
  101.           
  102.         let mes=this._toast.create({  
  103.         message'Task Deleted Successfully',  
  104.         duration2000,  
  105.         position'bottom'  
  106.         });  
  107.         this.allTask.splice(this.allTask.indexOf(t),1);  
  108.         mes.present();  
  109.       }  
  110.       else{  
  111.         let mes=this._toast.create({  
  112.         message'Error in deleting task',  
  113.         duration2000,  
  114.         position'bottom'  
  115.         });  
  116.         mes.present();  
  117.       }  
  118.     }  
  119.   );  
  120. }  
  121. }  
  122. [/code]   

I have created the tab application. So before running the application, I must change the default page i.e. go to tab page and change the home page to tasks page, as shown below 

  1. [code language=”typescript”]  
  2. import { Component } from '@angular/core';  
  3. import { HomePage } from '../home/home';  
  4. import { AboutPage } from '../about/about';  
  5. import { ContactPage } from '../contact/contact';  
  6. import { Tasks } from '../tasks/tasks';  
  7. @Component({  
  8.   templateUrl 'tabs.html'  
  9. })  
  10. export class TabsPage {  
  11.   // this tells the tabs component which Pages  
  12.   // should be each tab's root Page  
  13. tab1Root any = Tasks;  
  14.   tab2Root any = AboutPage;  
  15.   tab3Root any = ContactPage;  
  16.   constructor() {  
  17.   
  18.   }  
  19. }  
  20. [/code]   

So now, all is done and we're good to go and run the to-do application.

Run application in browser

cmd>ionic serve

Run application in device

cmd>ionic run android

But before running it on device, I need to add a platform for it.

cmd>ionic platform add android


Update Task




Add Task


Display Task


Delete Task