本文使用Ionic2從頭建立一個簡單的Todo應(yīng)用衬横,讓用戶可以做以下事情:
- 查看todo列表
- 添加新的todo項
- 查看todo詳情
- 保存 todo到持久化存儲
0 開始之前
本教程需要你了解基本的Ionic 2概念潘飘。已經(jīng)在電腦上安裝了Ionic 2。如果沒有答憔,先去安裝和學(xué)習(xí)吧挑庶。
1 創(chuàng)建新的Ionic 2工程
我們將通過生成一個基于“空白”模板的新項目開始拿霉。這是一個空的項目框架筋搏,但有一些示例代碼供我們使用。
運行以下命令創(chuàng)建新項目
ionic start ionic-todo blank --v2
一旦代碼生成,在文本編輯器打開項目糖权《赂梗可以看到Ionic 2項目的基本結(jié)構(gòu), 這些是由Ionic CLI生成的代碼温兼。
基本上,我們的應(yīng)用程序中的所有組件(我們的應(yīng)用程序?qū)⒂刹煌慕M件組成)將在** src ** 文件夾中(包括app文件夾中的根組件和在pages文件夾中我們所有的頁面組件)秸滴。一個組件將包括一個模板(.html文件),類定義(.ts文件)募判,或者一些樣式(.scss文件)荡含。同組件類似,您還可能創(chuàng)建諸如服務(wù)services(如稍后我們將創(chuàng)建的數(shù)據(jù)服務(wù))届垫,但沒有模板和樣式释液,但在結(jié)構(gòu)上類似一個正常的組件。這些服務(wù)也被稱作“providers”將被放置在一個providers文件夾装处。
現(xiàn)在,只有一個HomePage組件,設(shè)置一個虛擬視圖误债。在我們的應(yīng)用程序中我們要修改這個來顯示的所有待辦事項列表浸船。
先從自動生成的**src/app/app.component.ts文件開始來看一下:
import { Component } from '@angular/core';
import { Platform } from 'ionic-angular';
import { StatusBar } from 'ionic-native';
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(() => {
// Okay, so the platform is ready and our plugins are available.
// Here you can do any higher level native things you might need.
StatusBar.styleDefault();
});
}
}
app.component.ts文件中定義了根組件(root component)。相比其他組件該組件是特殊的,因為它是第一個組件被加載到應(yīng)用程序,從那里我們可以顯示更多的組件,可以添加更多的組件等等寝蹈±蠲基本上,我們的應(yīng)用程序結(jié)構(gòu)就像一棵樹,根組件就是樹的根箫老。
因此,重要的是我們的根組件(root component)知道在哪里可以找到我們的HomePage主頁,因為需要將它設(shè)置為root page根頁面封字。注意,我們導(dǎo)入(importing)HomePage** 在這個文件主頁的頂部,然后在下面的代碼中設(shè)置它作為根頁面(** root page**):
rootPage: any = HomePage;
我們可以在構(gòu)造函數(shù)上面聲明變量,像上面這樣的使其成員變量 member variables,這意味著他們可以通過引用this.myVal在整個類中被被訪問耍鬓,同時阔籽,它也將在您的模板中可用。** : any ** 只是一個TypeScript語言的內(nèi)容牲蜀,意味著rootPage可以是任何(any)類型笆制。如果你不適應(yīng) TypeScript,并感到困惑涣达,那也不用擔(dān)心——你可以把類型拋開在辆,您的應(yīng)用程序仍然會工作的很好。我不會在本教程中使用類型度苔,除了依賴注入是不可替代的地方(我們將稍后介入)开缎。如果你想知道更多關(guān)于在Ionic 2中使用類型,應(yīng)該學(xué)習(xí)TypeScript或ECMAScript 6相關(guān)知識林螃。
root page 根頁面是您應(yīng)用程序顯示的第一個頁面,然后你可以從這里導(dǎo)航到其他頁面。改變Ionic 2應(yīng)用程序中的視圖可以通過改變這一根頁面,或** push ** 推或 pop彈出視圖俺泣。推一個視圖將會改變展現(xiàn)疗认,彈出它將刪除當(dāng)前視圖并回到前面的視圖。關(guān)于導(dǎo)航的更詳細(xì)的解釋,我推薦看看一個相關(guān)的Ionic 2導(dǎo)航指南伏钠。
2. 設(shè)置主頁(Home page)
現(xiàn)在我們已經(jīng)建立了基本的應(yīng)用程序横漏,讓故事開始吧。首先,讓我們建立todo列表模板熟掂。
2.1 創(chuàng)建模板
按照下面的內(nèi)容修改 src/pages/home/home.html :
<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>
注意這里使用的語法在列表中使用ngFor缎浇,創(chuàng)建了一個速記到嵌入的模板中。這樣就不用迭代輸出了:
<ion-item *ngFor="let item of items" (click)="viewItem(item)">{{item.title}}</ion-item>
根據(jù)DOM(文檔對象模型)赴肚,嵌入式模板將會為每個項(items)創(chuàng)建特定的數(shù)據(jù)素跺。所以,如果我們的items數(shù)組(稍后將定義在類定義)有4項,那么< ion-item >將渲染四次誉券。還要注意,我們使用的** let item 指厌,循環(huán)分配一個items數(shù)組項給item**。這允許我們引用其屬性,并傳遞到viewItem函數(shù)踊跟。
我們將標(biāo)題設(shè)置為Todos(待辦事項)!我們設(shè)計一個按鈕使用< ion-buttons >踩验。因為這里有個end屬性,按鈕將被放置在end的位置。不同屬性的行為可能會有所不同箕憾,取決于在什么平臺上運行牡借,以iOS為例,將end會將按鈕放到導(dǎo)航欄的右邊。還要注意,按鈕本身我們給它一個屬性的ion-button將會使用Ionic 2 的按鈕樣式袭异,而icon-only樣式將會讓按鈕只包含一個圖標(biāo)沒有文本钠龙。
我們使用** (click) ** 來附加一個點擊監(jiān)聽器到這個元素,這里將在在home.ts中調(diào)用addItem()函數(shù)扁远。
2.2 創(chuàng)建類
按如下修改src/pages/home/home.ts:
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(){
}
}
還記得大明湖畔的夏雨荷嗎俊鱼?哦不是,還記得之前我們?nèi)绾谓ohomePage分配一個any類型變量嗎?現(xiàn)在我們在構(gòu)造函數(shù)中分配一個NavController類型給navCtrl參數(shù)畅买。這就是Ionic 2 的依賴注入工作模式并闲,基本上是一種方式告訴應(yīng)用程序“我們希望通過navCtrl引用到NavController”。通過添加公共關(guān)鍵字在它面前,它會自動創(chuàng)建一個成員變量谷羞。這意味著我們現(xiàn)在可以引用NavController通過在類里任意使用this.navCtrl帝火。
現(xiàn)在我們已經(jīng)建立了一些假的數(shù)據(jù)(我們使用ionViewDidLoad生命周期鉤子,這將在頁面加載時被觸發(fā))湃缎,您應(yīng)該能夠看到它已經(jīng)在列表中渲染了:
在運行** ionic serve ** 時犀填,因為既然我們導(dǎo)入了NavController服務(wù),我們就可以在這個組件push或pop視圖嗓违,如下所示:
this.navCtrl.push(SOME_PAGE);
或者
ionic g page AddItemPage
我們已經(jīng)創(chuàng)建了添加和查看項目的方法九巡,在更進(jìn)一步之前我們不得不先創(chuàng)建 AddItemPage andItemDetailPage 組件。
2.3 添加項目
我們將要創(chuàng)建一個新組件讓我們添加新的todo項蹂季。當(dāng)然冕广,這只是一個簡單的表單提供了標(biāo)題和描述來創(chuàng)建todo。
運行如下命令來生成一個add-item頁面
ionic g page AddItemPage
任何時候當(dāng)我們創(chuàng)建一個新頁面偿洁,我們需要確保該頁面被導(dǎo)入(imported)到我們的 app.module.ts撒汉,然后在entryComponents和declarations數(shù)組中被聲明。
按如下修改 src/app/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-page/add-item-page';
@NgModule({
declarations: [
MyApp,
HomePage,
AddItemPage
],
imports: [
IonicModule.forRoot(MyApp)
],
bootstrap: [IonicApp],
entryComponents: [
MyApp,
HomePage,
AddItemPage
],
providers: []
})
export class AppModule {}
就像上次涕滋,我們先創(chuàng)建組件的模版睬辐。
2.4 建立新增項目模版
按如下內(nèi)容修改 src/pages/add-item-page/add-item-page.html :
<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>
這里沒有什么太瘋狂的開始。這次我們定義了另一個按鈕,簡單地調(diào)用了定義在add-item-page.ts中的saveItem函數(shù)宾肺。我們還有另一個按鈕指向一個close方法——因為這個頁面作為一個Mode模式的頁面溯饵,我們希望能把頁面關(guān)閉,所以我們也會在add-item-page.ts定義這個方法锨用。
現(xiàn)在我們有一些輸入框了瓣喊,它們又有[(ngModel)]屬性,這個就是雙向綁定黔酥。任何作用到title字段的改變都將立即影響到add-tiem-page.ts(我們馬上要講到)里面的this.title成員變量藻三。反之亦然洪橘,任何this.title上的改變都將立即影響到模版。
同樣注意到我們的保存按鈕上使用了full屬性棵帽,這個方便的小屬性幫助我們設(shè)置按鈕寬度為full熄求。
2.5 建立添加項的類
現(xiàn)在我們將要建立一個類給我們的添加項組件。
按如下內(nèi)容修改 add-item-page.ts:
import { Component } from '@angular/core';
import { NavController, ViewController } from 'ionic-angular';
@Component({
selector: 'page-add-item-page',
templateUrl: 'add-item-page.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();
}
}
這里我們導(dǎo)入了一個怪異的服務(wù):ViewController逗概,可以用于模態(tài)(Modals)頁面的關(guān)閉(dismiss)弟晚。
除此之外,我們創(chuàng)建了saveItem函數(shù)來創(chuàng)建newItem對象,它使用當(dāng)前的標(biāo)題和描述值(即我們建立雙向數(shù)據(jù)綁定,無論用戶輸入什么)逾苫,然后我們關(guān)閉視圖卿城,同時我們也傳入了newItem在dismiss方法中。這將允許我們建立一個偵聽器铅搓,當(dāng)回到主頁(就是那個啟動這個頁面的另外一個頁面)時獲取數(shù)據(jù)瑟押。通過這種方式,我們可以從一個頁面?zhèn)鬟f數(shù)據(jù)到另一個頁面(然而,記住星掰,模態(tài)不需要在頁面之間傳遞數(shù)據(jù))多望。
2.6 在主頁保存新增項
就像我提到的,我們把要保存的數(shù)據(jù)返回發(fā)送給HomePage氢烘。我們現(xiàn)在導(dǎo)入import我們新增的AddItemPage組件到HomePage怀偷,當(dāng)用戶點擊新增時我們就創(chuàng)建出該視圖。
按如下內(nèi)容修改 src/pages/home/home.ts :
import { Component } from '@angular/core';
import { ModalController, NavController } from 'ionic-angular';
import { AddItemPage } from '../add-item-page/add-item-page'
@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){
}
}
你看上面這個文件的頂部播玖,可以發(fā)現(xiàn)我們現(xiàn)在導(dǎo)入import了AddItemPage組件椎工。這時我們就可以用這個頁面創(chuàng)建模態(tài)頁面了,具體看addItem方法蜀踏。注意我們這里建立了一個onDidDismiss監(jiān)聽器晋渺,這樣就可以獲取模態(tài)關(guān)閉時回傳的數(shù)據(jù),并通過saveItem方法保存∨д叮現(xiàn)在,我們僅通過將數(shù)據(jù)push到items數(shù)組畴栖,最終随静,我們將保存到數(shù)據(jù)庫。
我們已經(jīng)移除了假數(shù)據(jù)吗讶,因為現(xiàn)在用戶輸入通過saveItem方法被添加到了this.items燎猛。我們將items初始為空。
2.7 查看項目
現(xiàn)在照皆,我們想要一個功能重绷,就是用戶點擊todo列表里面的某一項,然后可以看到該項的細(xì)節(jié)信息(例如:這里只有描述可以看了膜毁,實際可以根據(jù)需要擴展昭卓,呵呵)愤钾。要做這個我們應(yīng)該知道這是又要創(chuàng)建一個新組件了啊。
還記得如何創(chuàng)建頁面嗎候醒,運行下面的代碼創(chuàng)建一個 item-detail 頁面:
ionic g page ItemDetailPage
time and time again能颁,我們需要在 app.module.ts 文件中設(shè)置一下,三件事:import倒淫,declarations伙菊, entryComponents:
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-page/add-item-page';
import { ItemDetailPage } from '../pages/item-detail-page/item-detail-page';
@NgModule({
declarations: [
MyApp,
HomePage,
AddItemPage,
ItemDetailPage
],
imports: [
IonicModule.forRoot(MyApp)
],
bootstrap: [IonicApp],
entryComponents: [
MyApp,
HomePage,
AddItemPage,
ItemDetailPage
],
providers: []
})
export class AppModule {}
按照順序是該寫模版了,開始:
千篇一律敌土,按照下面的內(nèi)容自行修改 src/pages/item-detail-page/item-detail-page.html :
<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>
相比其他模版镜硕,這里相當(dāng)?shù)闹卑住N覀冎皇鞘褂?lt; ion-card >指令簡單裝飾下返干,并輸出標(biāo)題和描述兴枯,值將在item-detail-page.ts中定義。
好犬金,繼續(xù)按照下面的內(nèi)容自行修改 src/pages/item-detail-page/item-detail-page.ts :
import { Component } from '@angular/core';
import { NavParams } from 'ionic-angular';
@Component({
selector: 'page-item-detail-page',
templateUrl: 'item-detail-page.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;
}
}
當(dāng)我們把這個頁面將傳入的數(shù)據(jù)項,點擊,然后我們把物品的標(biāo)題和描述,使用NavParams念恍。
現(xiàn)在我們要做的是在home.ts 內(nèi)設(shè)置 viewItem 函數(shù)和導(dǎo)入新的細(xì)節(jié)頁面。
src/pages/home/home.ts 修改如下:
viewItem(item){
this.navCtrl.push(ItemDetailPage, {
item: item
});
}
添加的導(dǎo)入代碼放在 src/pages/home/home.ts 的頂部:
import { ItemDetailPage } from '../item-detail-page/item-detail-page';
這時就可以push出項目的細(xì)節(jié)頁面晚顷,然后傳入被點擊的項目峰伙。如果你現(xiàn)在點擊存在于列表中的項目,你可能看到如下界面:
3 持久化數(shù)據(jù)保存
Todo應(yīng)用程序現(xiàn)在將基本工作,但數(shù)據(jù)沒有被存儲在任何地方只要你刷新應(yīng)用程序你將失去你所有的數(shù)據(jù)(不理想)该默。
現(xiàn)在我們要做的是創(chuàng)建一個服務(wù)被稱為Data用來處理存儲和檢索數(shù)據(jù)瞳氓。我們將使用Ionic 2提供的Stroage服務(wù)來幫助我們做到這一點。Stroage服務(wù)是Ionic 2的通用存儲服務(wù),它負(fù)責(zé)存儲數(shù)據(jù)的最佳方式,同時提供了一致的API供我們使用栓袖。
這意味著,如果您正在設(shè)備上運行匣摘,安裝了SQLite插件,那么它將使用一個本地SQLite數(shù)據(jù)庫進(jìn)行存儲裹刮,否則它將退回到使用基于瀏覽器的存儲(可能被操作系統(tǒng)擦除)音榜。
運行下面代碼創(chuàng)建服務(wù)
ionic g provider Data
data.ts 代碼修改如下:
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);
}
}
這個是有點不同于我們已經(jīng)創(chuàng)建的組件(它可能更合適認(rèn)為是service)。我們不使用@component裝飾捧弃,而使用@Injectable聲明這個類赠叼。
在構(gòu)造函數(shù)中,我們建立一個 Storage 服務(wù)的引用。
數(shù)組中save函數(shù)簡單地將所有的項放入數(shù)組并保存到存儲违霞,每當(dāng)項目變化我們將調(diào)用這個函數(shù)嘴办。
我們還將需要設(shè)置的Storage服務(wù),以及 Data provider,在我們 app.module.ts 文件。
src/app/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-page/add-item-page';
import { ItemDetailPage } from '../pages/item-detail-page/item-detail-page';
import { Storage } from '@ionic/storage';
import { Data } from '../providers/data';
@NgModule({
declarations: [
MyApp,
HomePage,
AddItemPage,
ItemDetailPage
],
imports: [
IonicModule.forRoot(MyApp)
],
bootstrap: [IonicApp],
entryComponents: [
MyApp,
HomePage,
AddItemPage,
ItemDetailPage
],
providers: [Storage, Data]
})
export class AppModule {}
請注意,我們已經(jīng)聲明這些在providers的數(shù)組,而不是declarations或entryComponents數(shù)組买鸽。
現(xiàn)在我們需要更新涧郊。ts使用這項新服務(wù)。
src/pages/home/home.ts 文件修改如下:
import { Component } from '@angular/core';
import { ModalController, NavController } from 'ionic-angular';
import { AddItemPage } from '../add-item-page/add-item-page'
import { ItemDetailPage } from '../item-detail-page/item-detail-page';
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
});
}
}
這是我們最后的一些代碼眼五。再次,我們importing數(shù)據(jù)服務(wù),通過傳遞給構(gòu)造函數(shù)妆艘。我們依然設(shè)置 items 開始是空的彤灶,使用數(shù)據(jù)服務(wù)獲取數(shù)據(jù)。
重要的是要注意getData 返回promise而不是數(shù)據(jù)本身双仍。抓取的數(shù)據(jù)存儲是異步的枢希,這意味著我們的應(yīng)用程序?qū)⒗^續(xù)運行當(dāng)數(shù)據(jù)加載時。promise讓我們數(shù)據(jù)完成加載時執(zhí)行一些操作,而不需要暫停整個應(yīng)用程序朱沃。
最后,我們還添加一個調(diào)用save 函數(shù)保存在數(shù)據(jù)服務(wù)當(dāng)一個新的條目被添加“危現(xiàn)在該函數(shù)將馬上更新我們的新數(shù)據(jù)條目數(shù)組,但items也將被復(fù)制保存到數(shù)據(jù)服務(wù),以便下次我們回到應(yīng)用程序是可用逗物。
4 總結(jié)
在本教程中我們已經(jīng)介紹了如何實現(xiàn)很多Ionic 2應(yīng)用的常用功能:
- 創(chuàng)建視圖
- 監(jiān)聽和處理事件
- 視圖之間的導(dǎo)航
- 在視圖之間傳遞數(shù)據(jù)
- 建立雙向數(shù)據(jù)綁定
- 保存數(shù)據(jù)
顯然還有很多我們可以做搬卒,使這個應(yīng)用程序更漂亮,添加刪除和編輯筆記的能力等等翎卓。