Ionic 2 Todo App

Build a Todo App from Scratch with Ionic 2 & 3



·

If you’ve been reading my beginner series of tutorials then you’ve probably seen a few example applications and had a bit of a play with Ionic 2.

When building an app from scratch though it’s a bit more intimidating, and you’ll probably run into a few roadblocks along the way. What I wanted to do in this tutorial was to come up with a concept for an application, and then build it from scratch using Ionic’s “blank” template.

So, to that end, we will be building a todo application that will allow a user to:

  • View a list of todo’s
  • Add new todo’s
  • View details of a specific todo (we will be setting up a Master / Detail pattern in Ionic 2)
  • Save the todo’s to permanent storage

Before We Get Started

Last updated for Ionic 3.0.0

Before you go through this tutorial, you should have at least a basic understanding of Ionic 2 concepts. You must also already have Ionic 2 installed on your machine.

If you’re not familiar with Ionic 2 already, I’d recommend reading my Ionic 2 Beginners Guide first to get up and running and understand the basic concepts. If you want a much more detailed guide for learning Ionic 2, then take a look at Building Mobile Apps with Ionic 2.

1. Generate a New Ionic 2 Project

We’re going to start off by generating a new project based on the “blank” template. This is mostly an empty skeleton project, but there is a bit of example code in there that we will get rid of.

Run the following command to generate a new Ionic 2 application

ionic start ionic-todo blank --v2

Once the code is generated, open up the project in your text editor. I explain the structure of an Ionic 2 application in this post so take a look at that if you’d like some more background information about the code the Ionic CLI generates.

Basically, all of the components (our application will be made up of a bunch of different components) for our application will be in the src folder (including the root component in the app folder, and all of our page components in the pages folder). A component will consist of a template (.html file), class definition (.ts file) and maybe some styling (.scss file). As well as components, you might also create things like services here (like the data service we will create later) as well, which don’t have a template or styling, but are similar in structure to a normal component. These services are also refered to as “providers” and will be placed in a providers folder.

Right now, there is just a single HomePage component that sets up a dummy view. We are going to modify this to display a list of all the todo items in our application.

I want to start off by taking a look at the src/app/app.component.ts file that is auto generated:

import { Component } from '@angular/core';
import { Platform } from 'ionic-angular';

import { HomePage } from '../pages/home/home';

@Component({
  template: `<ion-nav [root]="rootPage"></ion-nav>`
})
export class MyApp {
  rootPage = HomePage;

  constructor(platform: Platform) {
    platform.ready().then(() => {

    });
  }
}

The app.component.ts file defines our root component. This component is special when compared to the rest of the components because it is the first component that will be added to the application, and from there we can display more components, which can then add even more components and so on. Basically, our application structure is like a tree with the root component at the root of the tree.

So, it's important that our root component knows where to find our HomePage as it will need to set it as the root page. Notice that we are importing the HomePage at the top of this file, and then setting it as the root page with the following code:

rootPage: any = HomePage;

We are able to declare variables above the constructor like this to make them member variables, meaning they will be accessible throughout the entire class by referencing this.myVar and it will also be available to your templates. The : any part is just a TypeScript thing, which says that rootPage can be any type. If you’re not comfortable with TypeScript yet and are confused by this, don’t worry about it – you can leave types out and your application will still work fine. I won’t be using types in this tutorial, except for dependency injection where it is quired (which we will get to later). If you would like to know more about using types in Ionic 2, you should read Ionic 2: TypeScript vs ECMAScript 6.

The root page is the first page that will be displayed in your application, and then you can navigate to other pages from there. To change views in an Ionic 2 application you can either change this root page, or push and pop views. Pushing a view will change to that view, and popping it will remove it and go back to the previous view. For a more detailed explanation of navigation in Ionic 2, I recommend taking a look at A Simple Guide to Navigation in Ionic 2.

2. Setting up the Home Page

Now that we’ve got our basic application set up, let’s start making things happen. First, let’s set up our todo lists template.

Creating the Template

Modify src/pages/home/home.html to reflect the following:

<ion-header>
  <ion-navbar color="secondary">
    <ion-title>
      Todos!
    </ion-title>
    <ion-buttons end>
      <button ion-button icon-only (click)="addItem()"><ion-icon name="add-circle"></ion-icon></button>
    </ion-buttons>
  </ion-navbar>
</ion-header>

<ion-content>
  <ion-list>
    <ion-item *ngFor="let item of items" (click)="viewItem(item)">{{item.title}}</ion-item>
  </ion-list>
</ion-content>

Take notice of the * syntax used here for the *ngFor in the list, this is shorthand for creating an embedded template. So rather than literally rendering out:

<ion-item *ngFor="let item of items" (click)="viewItem(item)">{{item.title}}</ion-item>

to the DOM (Document Object Model), an embedded template will be created for each items specific data. So, if our items array (which will be defined later in the class definition) had 4 items in it, then the <ion-item> would be rendered out 4 times. Also notice that we are using let item, which assigns a single element from the items array to item as we loop through the array. This allows us to reference its properties, and also pass it into the viewItem function.

We are also setting the title to Todos! and we are supplying a button using <ion-buttons>. Since we have supplied the end attribute, the button will be placed in the “end” position. Different attributes may behave differently depending on what platform they are running on, but on iOS for example, “end” will place the button to the right of the nav bar. Also note that on the button itself we give it an attribute of ion-button which will give the button the Ionic styling, as well as icon-only which styles buttons that only contain an icon and no text.

We are using (click) to attach a click listener to this element, which will call the addItem() function in home.ts (which doesn’t exist yet).

Creating the Class Definition

Now that we have our template sorted, we need to create the functions we are referencing in our HomePage class, and we also need to supply the items data (we will just set up some dummy data initially).

Modify src/pages/home/home.ts to reflect the following:

import { Component } from '@angular/core';
import { NavController } from 'ionic-angular';

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

  public items;

  constructor(public navCtrl: NavController) {

  }

  ionViewDidLoad(){

    this.items = [
      {title: 'hi1', description: 'test1'},
      {title: 'hi2', description: 'test2'},
      {title: 'hi3', description: 'test3'}
    ];

  }

  addItem(){

  }

  viewItem(){

  }

}

Remember before how we assigned a type of any to the homePage variable? Now we are assigning a type of NavController to the navCtrl parameter in the constructor. This is how dependency injection works in Ionic 2, and is basically a way of telling the application “we want navCtrl to be a reference to NavController“. By adding the public keyword in front of it, it automatically creates a member variable for us. This means that we can now reference the NavController anywhere in this class by using this.navCtrl.

Now that we’ve set up some dummy data (we use the ionViewDidLoad lifecycle hook to do this, which is triggered as soon as the page is loaded), you should be able to see it rendered in your list:

Ionic 2 Todo App

when you run ionic serve. Since we have imported the NavController service, we will be able to push and pop views from this component by using:

this.navCtrl.push(SOME_PAGE);

and

this.navCtrl.pop();

We’ve created functions for adding items and viewing items, but before we can take this any further we are going to have to create a AddItemPage and ItemDetailPage component.

Adding Items

We’re going to create a new component now that will allow us to add new todo’s. This will be a simple form where we supply a title and description to create a new todo.

Run the following command to generate a new add-item page:

ionic g page AddItem

Whenever we create a new page, we need to ensure that it is imported into our app.module.ts, and declared in the declarations and entryComponents arrays.

Modify src/app/app.module.ts to reflect the following:

import { BrowserModule } from '@angular/platform-browser';
import { NgModule, ErrorHandler } from '@angular/core';
import { IonicApp, IonicModule, IonicErrorHandler } from 'ionic-angular';
import { MyApp } from './app.component';
import { HomePage } from '../pages/home/home';
import { AddItemPage } from '../pages/add-item/add-item';

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

Just like last time, let’s create our template for the component first.

Setting up the Add Item Template

Modify src/pages/add-item/add-item.html to reflect the following:

<ion-header>
  <ion-toolbar color="secondary">
    <ion-title>
      Add Item
    </ion-title>
      <ion-buttons end>
        <button ion-button icon-only (click)="close()"><ion-icon name="close"></ion-icon></button>
      </ion-buttons>
    </ion-toolbar>
</ion-header>

<ion-content>
  <ion-list>

    <ion-item>
      <ion-label floating>Title</ion-label>
      <ion-input type="text" [(ngModel)]="title"></ion-input>
    </ion-item>

    <ion-item>
      <ion-label floating>Description</ion-label>
      <ion-input type="text" [(ngModel)]="description"></ion-input>
    </ion-item>

  </ion-list>

  <button full ion-button color="secondary" (click)="saveItem()">Save</button> 

</ion-content>

There’s nothing too crazy going on here to start off with. We have another button defined, this time calling a saveItem function that we will define in add-item.ts shortly. We also have a button that references a close function – since we will eventually launch this page as a Modal we want the ability to dismiss the page, so we will also be defining this function in add-item.ts.

Then we have some inputs, and on them, we have [(ngModel)], which sets up two-way data binding for us. Any change we make to the title field will be immediately reflected on the this.title member variable in add-item.ts (which we will also add shortly). The same goes in reverse, any change we make to this.title in add-item.ts will also be immediately reflected in the template.

Also notice that on our save button we use the full attribute, this is a handy little attribute that will make the button full width for us.

Setting up the Add Item Class

Now we are going to set up the class for our Add Item component.

Modify add-item.ts to reflect the following:

import { Component } from '@angular/core';
import { NavController, ViewController } from 'ionic-angular';

@Component({
  selector: 'page-add-item',
  templateUrl: 'add-item.html'
})
export class AddItemPage {

  title;
  description;

  constructor(public navCtrl: NavController, public view: ViewController) {

  }

  saveItem(){

    let newItem = {
      title: this.title,
      description: this.description
    };

    this.view.dismiss(newItem);

  }

  close(){
    this.view.dismiss();
  }

}

We’re importing a weird new service here called ViewController which can be used with Modals to dismiss (close) them.

Apart from that, we are just creating the saveItem function which sets up a newItem object, which will use the current values of title and description (i.e. whatever the user puts in the input fields we set up two-way data binding on). We then dismiss the view, but we also pass in that new item to the dismiss function. This will allow us to set up a listener back on our home page (which will launch this page) to grab that item. In this way, we can pass data from one page to another (however, keep in mind that a modal isn’t required to pass data between pages).

Saving Items in the Home Page

As I just mentioned, we are sending some data back to the HomePage component to be saved, so we are going to have to set that up. We are also going to import our new AddItemPage component into the HomePage so that we can create that view when the user clicks Add Item.

Modify src/pages/home/home.ts to reflect the following:

import { Component } from '@angular/core';
import { ModalController, NavController } from 'ionic-angular';
import { AddItemPage } from '../add-item/add-item'

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

  public items = [];

  constructor(public navCtrl: NavController, public modalCtrl: ModalController) {

  }

  ionViewDidLoad(){

  }

  addItem(){

    let addModal = this.modalCtrl.create(AddItemPage);

    addModal.onDidDismiss((item) => {

          if(item){
            this.saveItem(item);
          }

    });

    addModal.present();

  }

  saveItem(item){
    this.items.push(item);
  }

  viewItem(item){

  }

}

If you look at the top of this file, you can see that we are now importing the AddItemPage component. This will allow us to create a Modal using that page, as we are doing in the addItem function. Notice that we also set up the onDidDismiss listener here, which will grab that item that is being passed back and then save it using the saveItem function. For now we just save an item by pushing it into the items array, but eventually we will save it into storage.

We’ve also removed the dummy data, because now when a user enters in a new item it will be added to this.items through the saveItem function. We also set items to be an empty array initially.

Viewing Items

Now we want to add the ability to click on a specific todo list item, and then view the details of that item (i.e. the description). To do this, we are going to create another new component.

Run the following command to create an item-detail page:

ionic g page ItemDetail

and once again, we will need to set it up in our app.module.ts file:

Modify src/app/app.module.ts to reflect the following:

import { BrowserModule } from '@angular/platform-browser';
import { NgModule, ErrorHandler } from '@angular/core';
import { IonicApp, IonicModule, IonicErrorHandler } from 'ionic-angular';
import { MyApp } from './app.component';
import { HomePage } from '../pages/home/home';
import { AddItemPage } from '../pages/add-item/add-item';
import { ItemDetailPage } from '../pages/item-detail/item-detail';

@NgModule({
  declarations: [
    MyApp,
    HomePage,
    AddItemPage,
    ItemDetailPage
  ],
  imports: [
    BrowserModule,
    IonicModule.forRoot(MyApp)
  ],
  bootstrap: [IonicApp],
  entryComponents: [
    MyApp,
    HomePage,
    AddItemPage,
    ItemDetailPage
  ],
  providers: [{provide: ErrorHandler, useClass: IonicErrorHandler}]
})
export class AppModule {}

Now let’s set up our new components template.

Modify src/pages/item-detail/item-detail.html to reflect the following

<ion-header>
  <ion-navbar color="secondary">
    <ion-title>
      {{title}}
    </ion-title>
  </ion-navbar>
</ion-header>

<ion-content>
  <ion-card>
    <ion-card-content>
      {{description}}
    </ion-card-content>
  </ion-card>
</ion-content>

Compared to the rest of the templates, this one is pretty straight forward. We’re just using the <ion-card> directive to fancy it up a bit, and outputting the values of title and description which will be defined in item-detail.ts.

Modify src/pages/item-detail/item-detail.ts to reflect the following:

import { Component } from '@angular/core';
import { NavParams } from 'ionic-angular';

@Component({
  selector: 'page-item-detail',
  templateUrl: 'item-detail.html'
})
export class ItemDetailPage {

  title;
  description;

  constructor(public navParams: NavParams){

  }

  ionViewDidLoad() {
    this.title = this.navParams.get('item').title;
    this.description = this.navParams.get('item').description;
  }

}

When we push this page we will pass in the data of the item that was clicked, and then we just set the title and description to that of the item using NavParams.

Now all we have to do is set up the viewItem function in home.ts and import the new detail page.

Modify the viewItem function in src/pages/home/home.ts to reflect the following:

  viewItem(item){
    this.navCtrl.push(ItemDetailPage, {
      item: item
    });
  }

Add the following line to the top of src/pages/home/home.ts:

import { ItemDetailPage } from '../item-detail/item-detail';

This will push the item detail page, and pass in the item in the list that was clicked. If you try clicking on an item in the list now, you should see something like this:

Ionic 2 Card

Saving Data Permanently with Storage

The todo application will basically work now, but the data isn’t being stored anywhere so as soon as you refresh the application you will lose all of your data (not ideal).

What we’re going to do now is create a service called Data that will handle storing and retrieving data for us. We will use the Storage service Ionic 2 provides to help us do this. Storage is Ionic’s generic storage service, and it handles storing data in the best way possible whilst providing a consistent API for us to use.

This means that if you are running on a device and have the SQLite plugin installed, then it will use a native SQLite database for storage, otherwise, it will fall back to using browser based storage (which can be wiped by the operating system).

Click here to learn more about the different storage options for HTML5 mobile applications.

Run the following command to generate a Data service:

ionic g provider Data

Modify data.ts to reflect the following:

import { Storage } from '@ionic/storage';
import { Injectable } from '@angular/core';

@Injectable()
export class Data {

  constructor(public storage: Storage){

  }

  getData() {
    return this.storage.get('todos');  
  }

  save(data){
    let newData = JSON.stringify(data);
    this.storage.set('todos', newData);
  }

}

This one is a little bit different to the rest of the components we have been creating (and it would be more appropriate to consider it a service). Instead of using the @Component decorator, we are instead declaring this class as an @Injectable.

In our constructor, we set up a reference to the Storage service.

Our save function simply takes in the array of all of the items and saves it to storage, whenever the items change we will call this function.

We will also need to set up the Storage service (which requires us to include a new import in the @NgModule), as well as the Data provider, in our app.module.ts file.

Modify src/app/app.module.ts to reflect the following:

import { BrowserModule } from '@angular/platform-browser';
import { NgModule, ErrorHandler } from '@angular/core';
import { IonicApp, IonicModule, IonicErrorHandler } from 'ionic-angular';
import { IonicStorageModule } from '@ionic/storage';
import { MyApp } from './app.component';
import { HomePage } from '../pages/home/home';
import { AddItemPage } from '../pages/add-item/add-item';
import { ItemDetailPage } from '../pages/item-detail/item-detail';
import { Data } from '../providers/data';

@NgModule({
  declarations: [
    MyApp,
    HomePage,
    AddItemPage,
    ItemDetailPage
  ],
  imports: [
    BrowserModule,
    IonicModule.forRoot(MyApp),
    IonicStorageModule.forRoot()
  ],
  bootstrap: [IonicApp],
  entryComponents: [
    MyApp,
    HomePage,
    AddItemPage,
    ItemDetailPage
  ],
  providers: [Data, {provide: ErrorHandler, useClass: IonicErrorHandler}]
})
export class AppModule {}

Notice that we have declared these services in the providers array, rather than in the declarations or entryComponents arrays.

Now we need to update home.ts to make use of this new service.

Modify src/pages/home/home.ts to reflect the following:

import { Component } from '@angular/core';
import { ModalController, NavController } from 'ionic-angular';
import { AddItemPage } from '../add-item/add-item'
import { ItemDetailPage } from '../item-detail/item-detail';
import { Data } from '../../providers/data';

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

  public items = [];

  constructor(public navCtrl: NavController, public modalCtrl: ModalController, public dataService: Data) {

    this.dataService.getData().then((todos) => {

      if(todos){
        this.items = JSON.parse(todos); 
      }

    });

  }

  ionViewDidLoad(){

  }

  addItem(){

    let addModal = this.modalCtrl.create(AddItemPage);

    addModal.onDidDismiss((item) => {

          if(item){
            this.saveItem(item);
          }

    });

    addModal.present();

  }

  saveItem(item){
    this.items.push(item);
    this.dataService.save(this.items);
  }

  viewItem(item){
    this.navCtrl.push(ItemDetailPage, {
      item: item
    });
  }

}

So here’s our final bit of code. Again, we’re importing the data service and passing it through to our constructor. We are still setting the items to be empty to start off with, and fetching the data using the data service. If there are any items returned it will set items to that, but if there is not it will just set it to an empty array again.

It’s important to note here that getData returns a promise not the data itself. Fetching data from storage is asynchronous which means our application will continue to run while the data loads. A promise allows us to perform some action whenever that data has finished loading, without having to pause the whole application.

For more information on promises, check out Andrew McGivery’s post on the subject (keep in mind though that the promise syntax is different in Ionic 2).

Finally, we also add a call to the save function in the data service when a new item is being added. So now this function will update our items array with the new data straight away, but it will also save a copy to the data service so that it is available next time we come back to the application.

Summary

In this tutorial, we have covered how to implement a lot of common functionality in an Ionic 2 app like:

  • Creating views
  • Listening for and handling events
  • Navigating between views
  • Passing data between views
  • Setting up two-way data binding
  • Saving data

Obviously, there’s a lot more we could do with this application like making it prettier, adding the ability to delete and edit notes and so on. I will likely create a Part 2 for this tutorial at some point where we will cover this, but for now hopefully that should have given you a pretty decent introduction to creating apps with Ionic 2.

What to watch next...

  • void brain

    it runs fine on the browser but if i build it in Xcode the phone cannot find build/js/app.bundle.js and build/css/app.css . what’s the problem?

  • Pingback: How to Create a Data Model in Ionic 2 | HTML5 Mobile Tutorials | Ionic, Phaser, Sencha Touch & PhoneGap()

  • Pingback: Ionic 2: How to Use Google Maps & Geolocation | HTML5 Mobile Tutorials | Ionic, Phaser, Sencha Touch & PhoneGap()

  • Jeff

    Hi Josh,

    Thank you for the great tutorials in ionic.
    I’m trying to save my captured image using the phone camera to sqlite.
    If I store a string value, it works.

    Can you give me an advise why its not working in my code, storing the captured image/photo. Thank you.

    let camOptions = {
    quality: 50,
    destinationType: navigator.camera.DestinationType.DATA_URL,
    sourceType: navigator.camera.PictureSourceType.CAMERA,
    allowEdit: true,
    encodingType: navigator.camera.EncodingType.JPEG,
    targetWidth: 100,
    targetHeight: 100,
    popoverOptions: CameraPopoverOptions,
    saveToPhotoAlbum: false
    };

    navigator.camera.getPicture(function(imageURI) {
    let image = “data:image/jpeg;base64,” + imageURI;
    this.storage.set(“image”,image);
    this.storage.get(“image”).then((pic) => {
    let image = document.getElementById(‘photo’);
    image.src = pic;
    });
    }, function(err) {
    alert(“error” +err);

    }, camOptions);

  • michel perez

    I get this error in the browser when loading the records from the database Error trying to diff ‘[object Object]’ in [null], this is the full trace Error:

    Error trying to diff ‘[object Object]’

    at new BaseException (http://localhost:8100/build/js/app.bundle.js:26208:24)

    at DefaultIterableDiffer.diff (http://localhost:8100/build/js/app.bundle.js:28413:20)

    at NgFor.doCheck (http://localhost:8100/build/js/app.bundle.js:41023:41)

    at AbstractChangeDetector.ChangeDetector_ListPage_0.detectChangesInRecordsInternal (eval at (http://localhost:8100/build/js/app.bundle.js:33077:17), :62:48)

    at AbstractChangeDetector.detectChangesInRecords (http://localhost:8100/build/js/app.bundle.js:32421:19)

    at AbstractChangeDetector.runDetectChanges (http://localhost:8100/build/js/app.bundle.js:32398:15)

    at AbstractChangeDetector._detectChangesInViewChildren (http://localhost:8100/build/js/app.bundle.js:32490:19)

    at AbstractChangeDetector.runDetectChanges (http://localhost:8100/build/js/app.bundle.js:32402:15)

    at AbstractChangeDetector._detectChangesContentChildren (http://localhost:8100/build/js/app.bundle.js:32483:19)

    at AbstractChangeDetector.runDetectChanges (http://localhost:8100/build/js/app.bundle.js:32399:15)

  • Krunal Dholakiya

    Hi Josh,

    Can you please guide me how to write sql query to insert and get data.

    here is my code which I’m working on but dont know whether its correct or not, I’m not able to insert or add data

    import {Page, NavController,Storage, SqlStorage, IonicPlatform} from ‘ionic/ionic’;
    import {Component,CORE_DIRECTIVES,FORM_DIRECTIVES} from ‘angular2/angular2’;
    import “./hello-ionic.scss”;

    @Page({
    templateUrl: ‘app/hello-ionic/hello-ionic.html’,
    directives: [CORE_DIRECTIVES,FORM_DIRECTIVES]
    })
    export class HelloIonicPage {
    constructor(nav: NavController) {
    this.nav = nav;
    this.storage = new Storage(SqlStorage);
    }
    model = new Data(”,’+91′,”,”,”,”,”,”);

    saveUser(model) {
    this.storage.query(‘insert into users(name, code, contact, email, dob, annDate, locality, comment) values (?, ?, ?, ?, ?, ?, ?, ?)’, [model.name, model.code, model.contact, model.email, model.dob, model.annDate, model.locality, model.comment]).then((users) => {
    console.log(“users”,users);
    })
    }
    getUser() {
    this.storage.query(‘select * from users’).then((users => {
    console.log(“received data”,users);
    console.log(“received data”,this.users);
    });
    })
    }
    export class Data {
    constructor(
    public name: string,
    public code: number,
    public contact: number,
    public email: string,
    public dob: string,
    public annDate: string,
    public locality: string,
    public comment: string
    ) { }
    }

  • Pingback: Build a Todo App from Scratch with Ionic 2 [VIDEO TUTORIAL] - angular.org.il()

  • Hi Josh, thank you very much for this tutorial.

    Well, I’m building an app using tabs, and I’m having a issue, I’d like to push() a page when clicking on a button inside the tab item, but when I do so, the page opens inside the tab item. How can I make the button open a page over the tab or replacing it?

    Thank you again,
    Heitor

  • µlises

    I think there’s a small bug in data.js at line 23, item is not an array. If you only save one record, that will produce an error when the app load, because that record was saved as an object not as an array

    • andersonshalom

      You’re right! Thanks.

  • beaver

    Hi Josh,

    Great tutorial on Ionic 2. Took me some time cause when writing this comment ionic 2 beta was out, so i had to adjust a few things to get it to work. So far so good everything works except my save. When i give in title and description (i logged it in my console) i returns blank. but you see it takes up a place in the array. don’t know where i went wrong with the whole thing …

    https://gyazo.com/0548b9a5415207c1e76c1c8ebd78779c (screenshot of my app)

    Any help is welcome. Thanks in advance

  • Pingback: How to Use Pipes to Manipulate Data in Ionic 2 | HTML5 Mobile Tutorials | Ionic, Phaser, Sencha Touch & PhoneGap()

  • Pingback: How to Create a Directive in Ionic 2 – Parallax Header | HTML5 Mobile Tutorials | Ionic, Phaser, Sencha Touch & PhoneGap()

  • Dipak Mahapurkar

    Thanks for great tutorial. When i run on android device it will show only white screen. How i solve it?

  • Dirk Bertels

    Thanks for these excellent tutorials. I’m getting the following error when I click the ‘Add Item’ button. Seems to happen when it tries to render the AddItemPage:

    EXCEPTION: No provider for Item! (TextInput -> Item)

    Any ideas anyone?

    • I just updated the tutorial for the latest alpha, check the code for add-item.html above and it should solve your problem.

      • Dirk Bertels

        Many thanks Josh – Your swift response is very much appreciated! it works now 🙂

  • µlises

    Does anyone have some issues with the SQLite plugin? Sometimes when the app loads a console log says: Storage: SQLite plugin not installed, falling back to WebSQL and I have installed the plugin.. any ideas of what it migth be the problem?

  • M.B

    Hello,
    great tutorial.

    SO by installing SQLite plugin you mean just running this command “cordova plugin add https://github.com/litehelpers/Cordova-sqlite-storage.git” in the project folder without implementing the API used for SQLite plugin as described in ngCordova docs? since i can’t see where did you use it!

    Thanks

  • itsru1
  • Luís Cunha

    I’ve been unable to get the ionic serve command to run properly. After running ionic start ionic-todo blank –v2 I ran ionic-serve right away to test it out and I got this error log https://gist.github.com/MoPHL/565106075c4aacdc41fc , but basically it seems like a problem with the babel module. Any idea what could be the issue?

  • Dan

    Josh,

    I’m getting an error browser_adapter.ts:73 EXCEPTION: Error trying to diff ‘[object Object]’ in [items in [email protected]:16]

    Uploaded code here: https://github.com/audan2009/ionic2todo

    Can you tell what’s going on?

    • Dan

      Not sure if those was the right thing to do but I think an array was not being passed to this.items. So *ngFor wasn’t working. So, in the DataService provider constructor function I set “this.data = []” instead of “this.data = null”

      • JC Pepper

        Hi Dan,

        I set this.data = [] like you suggested, but it didn’t fix the error. Aside from that console error, my AddItem page layout breaks.

        Have you found a fix for the above mentioned errors?

        Thanks!

  • Hey everybody, I’ve just updated the tutorial for the latest beta, so this should solve any problems people were having!

  • Jim

    Thank you for this update. I’d struggled with the tutorial until you posted this latest version but now all is working well. Can you help with a problem? With ionic 1 I had no problem with errors appearing in the console of my browsers developer tools and save me a ton of time debugging. I’ve never got the same error functionality working with ionic 2. errors just don’t show up in the developer tools. Some do in the terminal with ‘ionic serve -c’. How do you debug in ionic 2?

  • Joe

    Hi, as soon as I add the Data in the get parameters static function of the home page, the app stops working with no errors displayed in the console. I even tried to install the SqlLite storage plugin with no success. any suggestions on what to look for?

    • Joe

      Never mind, I was using Edge. With chrome everything works fine.

  • Pingback: Using Angular 2 without Ionic 2: Part 1 | HTML5 Mobile Tutorials | Ionic, Phaser, Sencha Touch & PhoneGap()

  • Oluflourish Emmanuel

    Thanks for this article and the update.

  • Varshil Shah

    Hi Josh thanx this tutorial .
    But i get issue . i didn’t see any output in browser.
    when i replaced this code

    this.dataService.getData().then((todos) => {

    if(todos){
    this.items = JSON.parse(todos);
    }

    });to

    this.items = [
    {title: ‘hi1’, description: ‘test1’},
    {title: ‘hi2’, description: ‘test2’},
    {title: ‘hi3’, description: ‘test3’},
    ];

    then i see menu but i couldn’t see Add Item button.
    I don’t know why. please help me

  • etoileweb

    Hi, I adapted the storage part to a bookmarking app. So user can bookmark a wordpress post on his device and find it quickly later. The problem is that a new bookmark overrides the existing one so I always have one item it the storage. Also I’d like to test whether an item is already bookmark in order not to save it again. Any help please ? Secondly, I’m fetching data from a WordPress website via a REST API. And and I’d like to perform two things : 1. How to make the difference between recently fetched data from previous ones ? 2. When displaying all posts, how to test bookmarked ones and show them differently in the list loop? Thanks for your help.

  • Kevin Bezold

    Hi Josh,

    Thanks for putting this together. I’m just starting to work through it and I found an issue.
    In home.html,

    should be:

    in order to get the + sign to show up.

  • Kevin Bezold

    Good tutorial. It works in Chrome, but errors in Firefox with the following:

    EXCEPTION: Error: Uncaught (in promise): EXCEPTION: Error in ./HomePage class HomePage_Host – inline template:0:0
    ORIGINAL EXCEPTION: TypeError: win.openDatabase is not a function

    (and then a lot more errors)

  • Carlos Robson

    I’m trying with :
    Ionic Framework Version: 2.0.0-beta.11
    Ionic CLI Version: 2.1.0-beta.1

    But have some troubles, it was ok with the dummy data, but not work after create add-item pages;
    -ionic g page Add Item not work (same as: http://stackoverflow.com/questions/39543923/ionic-2-cant-generate-pages-via-the-cli-how-to-resolve-error);
    -ionic serve, just appears a blank page;
    -after create add-item manually in app/pages, the typescript compiller don’t recognize as a module;

  • 0rla

    Hi Josh,
    first of all thanks, great tutorial, congratulations.
    I just would mention that in the new rc.0 of ionic 2, SQLite storage already works behind the scenes of Storage module, which is not anymore delivered within ionic-angular.

    So if you want to change your code in order to be up to date with rc.0, just do something like:
    (providers/data.ts)

    import { Storage } from ‘@ionic/storage’
    import { Injectable } from ‘@angular/core’;

    @Injectable()
    export class Data {
    private storage;
    constructor() {
    this.storage = new Storage();
    }
    getData(){
    return this.storage.get(‘todos’);
    }
    save(data){
    let newData = JSON.stringify(data);
    this.storage.set(‘todos’, newData);
    }
    }

    And then:
    (app.module.ts)

    import { NgModule } from ‘@angular/core’;
    import { IonicApp, IonicModule } from ‘ionic-angular’;
    import { MyApp } from ‘./app.component’;
    import { HomePage } from ‘../pages/home/home’;
    import { AddItemPage } from ‘../pages/add-item/add-item’;
    import { ItemDetailPage } from ‘../pages/item-detail/item-detail’;
    import { Data } from ‘../providers/data’;

    @NgModule({
    […]
    providers: [Data] // add data service to providers
    })
    export class AppModule {}

    Cheers!

  • Hey all, this tutorial has been updated for the release candidate!

    • Daniel

      Thanks Josh!

  • Isaque Suzuki

    Hi @joshmorony:disqus , I think something is missing from saveItem method:
    saveItem(item) {
    this.items.push(item);
    this.dataService.save(this.items);
    }

    • Thanks for pointing that out – updated the post 🙂

      • Isaque Suzuki

        BTW, this tutorial is a great starting point. Thanks!

    • najlla rahim

      thanks for your help.

  • Adir Zoari

    thank you,can you explain how to do that with firebase database also?

  • Sam

    why ionic2 RC still uses the ‘Page’ naming convention instead of angular2 ‘Component’ naming convention ? i.e : HomePage instead of HomeComponent. Any idea ?

  • Oxide Debbarma

    first of all @joshmorony:disqus thank you for the tutorial.
    1. Does ‘ionViewDidLoad’ load automatically?
    2. also i get the warning “Native: tried calling StatusBar.styleDefault, but Cordova is not available. Make sure to include cordova.js or run in a device/simulator”.

  • Dwayne Patel

    How would you update a todo and save it to local service.
    I am making a sports app where you can select players from a list taken from the tutorial above. I have added a checkbox and when players are selected I want to open a new view populated with only players who have been selected.

  • Great Tutorial Josh. Simple and efficient

  • Stan Bordeaux

    Hi

    Nice intro but the data is not being saved. When I add a new item then refresh the app we come back with an empty array new item not saved. I even tried your source code to ensure no mistakes but still not saving??

    • Stan Bordeaux

      whoa, well I just scrolled down the list and discovered the answer from Isaque Suzuki. But your source code still not reflecting the vital bit about saving to the dataService. Could be a good idea to add that into the lesson 🙂

  • Muhammad

    Hey Josh! How is that part 2 coming along? There is worryingly low material on CRUD operations with Ionic 2’s Storage! As a fan of your tutorials, I demand you do your magic :D!

  • Andy

    Is there a follow on for this tute yet? Really interested in seeing the Edit and Delete functionality sorted too
    thanks!

  • najlla rahim

    how to edit and delete the list

  • Benjamin McMullan

    Thanks for the tut, I feel like I’ve made progress!

  • Sachin Devinagar

    Thanks for the tutorial.
    Im getting error like —
    “Runtime Error
    Can’t resolve all parameters for Storage: (?).”
    why?

  • Miguel Malcolm

    Great tutorial Josh. Thanks

  • MarynenkoA

    Hi, please help me … how to delete and edit item?