I’ve created a few tutorials around Ionic 2 while it was in its early alpha stage up until now. These tutorials explain how to use the bits and pieces that the framework or Angular offers, but I never demonstrated how to make a functional application. Seeing how to put the pieces together makes a huge difference when learning a new technology.
We’re going to see how to build a simple todo list type Android and iOS application using Ionic 2, Angular, and TypeScript.
To make things as easiest as possible to understand, we’re going to start with a fresh project and work our way up. Using the Command Prompt (Windows) or Terminal (Mac and Linux), execute the following:
ionic start TodoApp blank --v2
cd TodoApp
ionic platform add ios
ionic platform add android
There are a few things to note in the above commands. First you’ll notice the --v2
tag during the project creation. This means we will be creating an Ionic 2 project that uses TypeScript. It is only possible to use this tag if we have the correct Ionic CLI. The second thing to be aware of is around the iOS platform. We cannot build for the iOS platform unless we are using a Mac computer.
We are going to be creating a two page application, but for now we’re going to focus on the main page. This page will be responsible for showing the list of todo items. We’ll also be able to remove todo items from this page as well.
Create the following directories and files whatever way makes sense for you. I’m on a Mac, so I’m going to use mkdir
and touch
to do this from the Terminal.
mkdir app/pages/todos
touch app/pages/todos/todos.ts
touch app/pages/todos/todos.html
touch app/pages/todos/todos.scss
Let’s focus on the logic file first. Open the project’s app/pages/todos/todos.ts file and include the following code. Don’t worry, we’ll break it down after.
import {Page, NavController} from 'ionic-angular';
import {AddPage} from "../add/add";
@Page({
templateUrl: 'build/pages/todos/todos.html'
})
export class TodosPage {
public todoList: Array<string>;
constructor(private nav: NavController) { }
onPageDidEnter() {
this.todoList = JSON.parse(localStorage.getItem("todos"));
if(!this.todoList) {
this.todoList = [];
}
}
delete(index: number) {
this.todoList.splice(index, 1);
localStorage.setItem("todos", JSON.stringify(this.todoList));
}
add() {
this.nav.push(AddPage);
}
}
We haven’t created the page yet, but let’s go ahead and import it anyways. We’re not going to try to run the application right away so we will be fine. We’re also importing the NavController
which will allow us to navigate to the AddPage
that we’ve yet to create.
In the @Page
section we define the HTML file that will be paired with this particular TypeScript file.
This brings us to the actual TodosPage
class. We will have one public variable being our list of todo items. By making it public we can access it from the HTML file and render it to the screen.
constructor(private nav: NavController) { }
In the above constructor
method we aren’t actually initializing any variables, but we are defining the NavController
to be used throughout our application. You might be wondering why we aren’t initializing the todoList
variable in the constructor
method, but instead the following:
onPageDidEnter() {
this.todoList = JSON.parse(localStorage.getItem("todos"));
if(!this.todoList) {
this.todoList = [];
}
}
We’re using the framework reserved onPageDidEnter
method because we want to hit two birds with one stone. The page constructor
method will only fire on navigation push events, or in other words, navigate to events. When we try to navigate back, the constructor
won’t fire. The onPageDidEnter
event will trigger in both scenarios which is why we are reading the todo items from local storage in it. If there is nothing in local storage we will initialize an empty array.
delete(index: number) {
this.todoList.splice(index, 1);
localStorage.setItem("todos", JSON.stringify(this.todoList));
}
The next method we see is the delete
method. It will be used for deleting items in the list based on whatever index is provided. Using the splice
method we can delete an item at a particular index. Once this is done, we will re-serialize the array and store it in local storage.
add() {
this.nav.push(AddPage);
}
Finally we have the add
method. This is where we will perform the navigation to the AddPage
that we’ll create soon.
Now we can focus on creating the HTML file that goes with the TypeScript file we just created. Open the project’s app/pages/todos/todos.html and include the following markup:
<ion-header>
<ion-navbar>
<ion-title>Ionic 2 Example - List</ion-title>
<ion-buttons end>
<button (click)="add()"><ion-icon name="add"></ion-icon></button>
</ion-buttons>
</ion-navbar>
</ion-header>
<ion-content class="todos">
<ion-list>
<ion-item-sliding *ngFor="let todo of todoList; let i = index">
<ion-item>
<h2>{{ todo }}</h2>
</ion-item>
<ion-item-options>
<button danger (click)="delete(i)">
<ion-icon name="trash"></ion-icon>
Delete
</button>
</ion-item-options>
</ion-item-sliding>
</ion-list>
</ion-content>
Let’s break down the HTML file like we did the TypeScript file. So starting with the page’s navigation bar:
<ion-header>
<ion-navbar>
<ion-title>Ionic 2 Example - List</ion-title>
<ion-buttons end>
<button (click)="add()"><ion-icon name="add"></ion-icon></button>
</ion-buttons>
</ion-navbar>
</ion-header>
We define the page title and add a single button to the right side of the header. This button will only be an icon, but when it is pressed it will trigger the add
function that we created. This is made possible from the (click)
tag.
Now we’re brought into the core page content.
<ion-content class="todos">
<ion-list>
<ion-item-sliding *ngFor="let todo of todoList; let i = index">
<ion-item>
<h2>{{ todo }}</h2>
</ion-item>
<ion-item-options>
<button danger (click)="delete(i)">
<ion-icon name="trash"></ion-icon>
Delete
</button>
</ion-item-options>
</ion-item-sliding>
</ion-list>
</ion-content>
We have an ion-list
in the above, but it is not a standard list. Each element in the list will be a sliding item, meaning we can swipe it. We create each item in the list by looping through the public todoList
array. We also define our looping index as it will be relevant when it comes time to delete items.
When a list row is swiped, it will expose a button which a trash icon. When this button is pressed, the delete
method will be called and we will pass the index of the list item that was pressed.
When it comes to our stylesheet, open the project’s app/pages/todos/todos.scss and include the following class:
.todos { }
We aren’t using any special styles, but at least we know it is available to us should we decide we want to make some changes.
Now let’s have a look at creating the second page of our application. This page will be responsible for adding items to the local storage which will then be loaded in our main page. You don’t have to use local storage. You can use SQLite or another technology if you’d like, but for this example we’ll just stick with local storage.
Just like with the first page we need to create some directories and files. Again, I’ll be using mkdir
and touch
, but you create them however you want.
mkdir app/pages/add
touch app/pages/add/add.ts
touch app/pages/add/add.html
touch app/pages/add/add.scss
With everything created, open the project’s app/pages/add/add.ts file and include the following code:
import {Page, NavController} from 'ionic-angular';
@Page({
templateUrl: 'build/pages/add/add.html'
})
export class AddPage {
public todoList: Array<string>;
public todoItem: string;
constructor(private nav: NavController) {
this.todoList = JSON.parse(localStorage.getItem("todos"));
if(!this.todoList) {
this.todoList = [];
}
this.todoItem = "";
}
save() {
if(this.todoItem != "") {
this.todoList.push(this.todoItem);
localStorage.setItem("todos", JSON.stringify(this.todoList));
this.nav.pop();
}
}
}
Just like with the TodosPage
class, let’s break down this file as well.
In the @Page
section we are defining the HTML file that will be paired with this particular TypeScript file. Most of what we are doing happens in the AddPage
class.
In the class we have two variables, one public and one private. The todoList
variable is private because we don’t need to render it on the screen, but we do need to use it. The todoItem
variable is public because we do need to bind it to a form on the screen.
constructor(private nav: NavController) {
this.todoList = JSON.parse(localStorage.getItem("todos"));
if(!this.todoList) {
this.todoList = [];
}
this.todoItem = "";
}
Unlike the previous page, all of our initialization logic will happen in the constructor
method. This is because pop events will never happen on this page. You could use onPageDidEnter
if you really wanted to.
save() {
if(this.todoItem != "") {
this.todoList.push(this.todoItem);
localStorage.setItem("todos", JSON.stringify(this.todoList));
this.nav.pop();
}
}
In the save
method we first check to make sure the todoItem
variable has data in it. If it does, push it into the list and save it to local storage. When saving is complete we will pop back in the navigation stack to the previous page.
Now we can focus on the HTML file that is paired with this TypeScript file. Open the project’s app/pages/add/add.html file and include the following markup:
<ion-header>
<ion-navbar>
<ion-title>Ionic 2 Example - Create</ion-title>
<ion-buttons end>
<button (click)="save()"><ion-icon name="checkmark"></ion-icon></button>
</ion-buttons>
</ion-navbar>
</ion-header>
<ion-content class="add">
<ion-list>
<ion-item>
<ion-label floating>Todo Item</ion-label>
<ion-input type="text" [(ngModel)]="todoItem"></ion-input>
</ion-item>
</ion-list>
</ion-content>
Just like last time, we’ll break down the HTML to understand what is happening.
<ion-header>
<ion-navbar>
<ion-title>Ionic 2 Example - Create</ion-title>
<ion-buttons end>
<button (click)="save()"><ion-icon name="checkmark"></ion-icon></button>
</ion-buttons>
</ion-navbar>
</ion-header>
In the navigation header we have a title and a single button to the right of the title. This button will be a checkmark to represent it is used for saving the data. When the (click)
event is triggered, the save
method will be called in the TypeScript file.
<ion-content class="add">
<ion-list>
<ion-item>
<ion-label floating>Todo Item</ion-label>
<ion-input type="text" [(ngModel)]="todoItem"></ion-input>
</ion-item>
</ion-list>
</ion-content>
In the core content we have a list of input elements, or in our case a single input element. It is bound to the todoItem
variable in our TypeScript file via the [(ngModel)]
tag.
When it comes to our stylesheet, open the project’s app/pages/add/add.scss and include the following class:
.add { }
We aren’t using any special styling, but at least we know it is available to us.
We didn’t specifically add any styles, but we layed down the foundation for the future. This means we should add them to the theme collection.
Open the project’s app/theme/app.core.scss and include the following lines:
@import '../pages/todos/todos';
@import '../pages/add/add';
Now we can make our application more beautiful than it already is if we wanted to.
When we created our project we started with a HomePage
page. We aren’t using it so we want to replace it with the TodosPage
that we had created.
Open the project’s app/app.ts file and include the following code:
import {App, Platform} from 'ionic-angular';
import {StatusBar} from 'ionic-native';
import {TodosPage} from './pages/todos/todos';
@App({
template: '<ion-nav [root]="rootPage"></ion-nav>',
config: {}
})
export class MyApp {
rootPage: any = TodosPage;
constructor(platform: Platform) {
platform.ready().then(() => {
StatusBar.styleDefault();
});
}
}
Essentially all that changed was the references to HomePage
. After updating them, the application should be good to go.
You just saw how to create a simple Ionic 2 TypeScript application. It accomplished quite a bit. We saw how to navigate between pages, persist data, as well as delete data. As mentioned in the tutorial, if you wanted to use something beyond local storage, like SQLite, you can without any issues.
With this foundation, you should be able to take these skills and build a more complex application that is more useful to people in one of the app stores.