TODOアプリと作る
よく見かけるTODOアプリをIonic3で作っていきます。基本的には以下のサイトの写経ですが、データ保存のために、公式ドキュメントに書いてある Storage を利用します。
それから編集もできるようにします。
作ったものはgithubに有ります。
https://github.com/mntm0/ionic3_todo
プロジェクトの作成
まずはプロジェクトを空のテンプレートで作成します。
$ ionic start todo blank
とりあえず起動して確認します。
$ cd todo
$ ionic serve
一覧画面の作成
ホーム画面でTODO一覧が表示されるようにします。home.ts と home.html を次のように編集します。
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: 'title1', description: 'description1' },
{ title: 'title2', description: 'description2' },
{ title: 'title3', description: 'description3' }
];
}
}
<ion-header>
<ion-navbar color="primary">
<ion-title>
TODO
</ion-title>
</ion-navbar>
</ion-header>
<ion-content padding>
<ion-list>
<ion-item *ngFor="let item of items">
{{item.title}}
</ion-item>
</ion-list>
</ion-content>
この時点で画面は次のように表示されるはずです。
ionViewDidLoad
NavController – Ionic API Documentation – Ionic Framework
ionViewDidLoad関数は、ページのライフサイクルイベントの一種で、ページがロードされた時に発生するイベントです。
home.ts
ここではページロード時に、items というプロパティを更新しています。items が画面にバインドされているTODOオブジェクトの配列です。この値が更新されると、画面上のTODOリストが更新されます。
home.htmlは見ての通りなので割愛。
入力画面
一覧表示ができたので、次に入力画面を作成します。まずはCLIでページの雛形を作成します。
$ ionic generate page InputItem
おそらく input-item という名前でディレクトリが作成され、その下にファイル群が生成されています。input-item.ts, input-item.html をそれぞれ以下のように編集します。
import { Component } from '@angular/core';
import { IonicPage, NavController, NavParams, ViewController } from 'ionic-angular';
@IonicPage()
@Component({
selector: 'page-input-item',
templateUrl: 'input-item.html',
})
export class InputItemPage {
public title: string;
public description: string;
constructor(public navCtrl: NavController,
public navParams: NavParams,
public viewCtrl: ViewController) {
}
ionViewDidLoad() {
// 呼び出し時に引数が渡っていればそれを表示
let item = this.navParams.get('item');
if (item) {
this.title = item.title;
this.description = item.description;
}
}
saveItem() {
let inputItem = {
title: this.title,
description: this.description
};
// 入力された値を閉じる際に返す
this.viewCtrl.dismiss(inputItem);
}
close() {
this.viewCtrl.dismiss();
}
}
<ion-header>
<ion-navbar color="primary">
<ion-navbar>
<ion-title>AddItem</ion-title>
</ion-navbar>
<ion-buttons end>
<button ion-button icon-only (click)="close()">
<ion-icon name="close"></ion-icon>
</button>
</ion-buttons>
</ion-navbar>
</ion-header>
<ion-content padding>
<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="primary" (click)="saveItem()">Save</button>
</ion-content>
input-item.ts
入力画面をモーダルで表示させるために、ViewController を利用するので import しておきます。
この入力画面は編集時にも利用するので、表示時に引数で値を受け取ることにします。ionViewDidLoad関数の中で、NavControllerから引数を受け取っています。
ViewController – Ionic API Documentation – Ionic Framework
ViewController の dismiss() で画面を閉じます。閉じる際に引数で入力後の値を返しているのが、saveItem() で、close() は単に画面を閉じます。入力値の保存は、呼び出し元になる home.ts で行うことになります。
input-item.html
ここは特に難しいことはしておらず、単純に入力値がバインドされるように設定しているのと、close(), saveItem() がそれぞれ呼ばれるようにしています。
データの新規登録
一覧画面から入力画面を呼び出す処理を作成します。その前にまずは app.module.ts に追記します。import, declarations, entryComponents の3箇所です。
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 { MyApp } from './app.component';
import { HomePage } from '../pages/home/home';
import { InputItemPage } from '../pages/input-item/input-item';
@NgModule({
declarations: [
MyApp,
HomePage,
InputItemPage
],
imports: [
BrowserModule,
IonicModule.forRoot(MyApp)
],
bootstrap: [IonicApp],
entryComponents: [
MyApp,
HomePage,
InputItemPage
],
providers: [
StatusBar,
SplashScreen,
{provide: ErrorHandler, useClass: IonicErrorHandler}
]
})
export class AppModule {}
これで入力ページを呼び出すことができるようになります。まずは新規TODOの作成処理を作成してみます。次のとおりに home.ts, home.html を書き換えます。
import { Component } from '@angular/core';
import { NavController, ModalController } from 'ionic-angular';
import { InputItemPage } from '../input-item/input-item';
@Component({
selector: 'page-home',
templateUrl: 'home.html'
})
export class HomePage {
public items = [];
constructor(public navCtrl: NavController,
public modalCtrl: ModalController) {
}
ionViewDidLoad() {
}
createItem() {
// モーダルの生成
let addModal = this.modalCtrl.create(InputItemPage);
// dismissイベント(閉じられた時)の処理
addModal.onDidDismiss((item) => {
if(item) {
// 入力があればそれを追加する
this.items.push(item);
}
});
// モーダルの表示
addModal.present();
}
}
<ion-header>
<ion-navbar color="primary">
<ion-title>
TODO
</ion-title>
<ion-buttons end>
<button ion-button icon-only (click)="createItem()">
<ion-icon name="add"></ion-icon>
</button>
</ion-buttons>
</ion-navbar>
</ion-header>
<ion-content padding>
<ion-list>
<ion-item *ngFor="let item of items">
{{item.title}}
</ion-item>
</ion-list>
</ion-content>
home.ts
コンストラクタの初期データはもう不要なので削除します。代わりに createItem() を作成し、そこで入力画面をモーダルで表示する処理を作成します。onDidDismissで入力値が帰ってきていた場合、それを表示用の items に追加しています。
home.html
ナビゲーションバーに createItem() を呼び出すボタンを追加しています。
以上の処理が書けたら表示して、画面上からデータをできるようになっています。
データの編集
続けてデータの編集です。home.ts に editItem() を追加します。home.html で、これをクリックイベントで呼び出すように追加します。
editItem(item) {
const index = this.items.indexOf(item);
// モーダルの生成(引数にitemを渡す)
let editItem = this.modalCtrl.create(InputItemPage, {
item: item
});
// dismissイベント(閉じられた時)の処理
editItem.onDidDismiss((item) => {
if(item) {
this.items[index] = item;
}
});
// モーダルの表示
editItem.present();
}
<ion-header>
<ion-navbar color="primary">
<ion-title>
TODO
</ion-title>
<ion-buttons end>
<button ion-button icon-only (click)="createItem()">
<ion-icon name="add"></ion-icon>
</button>
</ion-buttons>
</ion-navbar>
</ion-header>
<ion-content padding>
<ion-list>
<ion-item *ngFor="let item of items" (click)="editItem(item)">
{{item.title}}
</ion-item>
</ion-list>
</ion-content>
データの作成と同じような感じですが、インデックスを保持して入力内容の item で置き換えてます。
データの削除
データの削除を行うためのボタンを追加して処理を追加します。home.ts に deleteItem() を追加します。home.html にボタンを追加して、これを呼び出します。
deleteItem(item) {
const index = this.items.indexOf(item);
this.items.splice(index, 1);
}
<ion-header>
<ion-navbar color="primary">
<ion-title>
TODO
</ion-title>
<ion-buttons end>
<button ion-button icon-only (click)="createItem()">
<ion-icon name="add"></ion-icon>
</button>
</ion-buttons>
</ion-navbar>
</ion-header>
<ion-content padding>
<ion-list>
<ion-item *ngFor="let item of items" (click)="editItem(item)">
{{item.title}}
<button ion-button color="danger" item-end (click)="deleteItem(item)">
<ion-icon name="trash">Delete</ion-icon>
</button>
</ion-item>
</ion-list>
</ion-content>
TODOのようなものが完成
たぶんここまでで単純なTODOのようなものができているはずです。
ストレージにデータを保存したい
データをストレージに保存するようにしたいと思います。アプリを終了しても、データが保持されるようにします。基本的には公式ドキュメントに沿います。
まずは必要なものをインストールします。
$ ionic cordova plugin add cordova-sqlite-storage
$ npm install --save @ionic/storage
app.module.ts に追記します。
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 { IonicStorageModule } from '@ionic/storage';
import { MyApp } from './app.component';
import { HomePage } from '../pages/home/home';
import { InputItemPage } from '../pages/input-item/input-item';
@NgModule({
declarations: [
MyApp,
HomePage,
InputItemPage
],
imports: [
BrowserModule,
IonicModule.forRoot(MyApp),
IonicStorageModule.forRoot()
],
bootstrap: [IonicApp],
entryComponents: [
MyApp,
HomePage,
InputItemPage
],
providers: [
StatusBar,
SplashScreen,
{provide: ErrorHandler, useClass: IonicErrorHandler}
]
})
export class AppModule {}
あとは home.ts にストレージでデータの処理を書くようにします。ページロード時にストレージからTODOのデータを読み出して表示し、データ更新後にストレージのデータを更新しています。
import { Component } from '@angular/core';
import { NavController, ModalController } from 'ionic-angular';
import { Storage } from '@ionic/storage';
import { InputItemPage } from '../input-item/input-item';
@Component({
selector: 'page-home',
templateUrl: 'home.html'
})
export class HomePage {
public items = [];
constructor(public navCtrl: NavController,
public modalCtrl: ModalController,
private storage: Storage) {
}
ionViewDidLoad() {
// 初期データをストレージから読み込んで表示
this.storage.get('items').then((items) => {
this.items = items ? items : [];
});
}
createItem() {
// モーダルの生成
let addModal = this.modalCtrl.create(InputItemPage);
// dismissイベント(閉じられた時)の処理
addModal.onDidDismiss((item) => {
if(item) {
// 入力があればそれを追加する
this.items.push(item);
this.updateStorageData();
}
});
// モーダルの表示
addModal.present();
}
editItem(item) {
const index = this.items.indexOf(item);
// モーダルの生成(引数にitemを渡す)
let editItem = this.modalCtrl.create(InputItemPage, {
item: item
});
// dismissイベント(閉じられた時)の処理
editItem.onDidDismiss((item) => {
if(item) {
this.items[index] = item;
this.updateStorageData();
}
});
// モーダルの表示
editItem.present();
}
deleteItem(item) {
const index = this.items.indexOf(item);
this.items.splice(index, 1);
this.updateStorageData();
}
updateStorageData() {
// ストレージのデータを更新
this.storage.set('items', this.items).then(() => {
});
}
}
後付の無理矢理感はありますが、やりたいことは実現できています。ストレージは key/value のペアを保存する方式なので、RDBのような使い方はできません。内部でデータがどのように保存されるかは、プラグイン側がうまくやってくれるようですが、基本的にはSQLiteに保存されているみたいです。driver というプロパティ値で確認できます。
かなりお手軽に実装できるのでちょっとしたデータ保存には最適だと思われます。
ネイティブの機能として SQLite をプラグインで使用することもできるみたいです。
以上。
コメントを書く