iT邦幫忙

2017 iT 邦幫忙鐵人賽
DAY 23
0
Modern Web

Angular 2 之 30 天邁向神乎其技之路系列 第 23

[Day 23] Angular 2 + Ionic = Mobile App ( 3 ) 實作 Todo List

建立專案

上回我們已經說明如何建立一個專案,而這次我們用經典的 Todo List (備忘錄) 來做說明

ionic start TODOApp blank --v2

blank 就是空白的模板

上回介紹過 Ionic 專案的架構,今天我們都會著重在 scr 的部分,也就是程式主幹。

成品

先看看結果

這邊可以看到我完成的專案

建立頁面

scr/pages 是我們的 App 裡面有的頁面,App 的各種畫面,就等同於網頁的各個頁面。

一個簡單的 TODO,包含:

  • 首頁:呈現所有事項
  • 細節:檢視詳細內容
  • 增加:增加事項的畫面

空白專案已經有 scr/pages/home 了, home 就是我們的首頁,我們還缺一個 detail 跟 add
先 cd 進入 TODOApp 資料夾後,我們可以在終端機輸入

ionic generate page detail

generate 可以減寫成 g,自動產生 html、ts、scss 三個檔案

也可以手動輸入

mkdir app/pages/detail
touch app/pages/detail/detail.ts
touch app/pages/detail/detail.html
touch app/pages/detail/detail.scss

差別在於 ionic 指令會產生套好模板的文件,手動建立則是空白的。

接著

ionic generate page add

首頁 home

html

首先來看 app/pages/todos/home.html,這是首頁的模板

<ion-header>
    <ion-navbar>
        <ion-title>Todo List</ion-title>
        <ion-buttons end>
            <button (click)="add()" ion-button icon-only><ion-icon name="add"></ion-icon></button>
        </ion-buttons>
    </ion-navbar>
</ion-header>
 
<ion-content>
    <ion-list>
        <ion-item-sliding *ngFor="let todo of todoList; let i = index;">
            <button ion-item (click)="detail(i)">
                <h2>{{ todo.title }}</h2>
            </button>
            <ion-item-options>
                <button class="red" (click)="delete(i)">
                    <ion-icon name="trash"></ion-icon>
                    Delete
                </button>
            </ion-item-options>
        </ion-item-sliding>
    </ion-list>
</ion-content>

add() 是之後用來加入新事項的按鈕。
delete(i) 是之後用來刪掉的按鈕。
detail(i) 點進去可以看細節。

ts

一個畫面是一個組件,概念就是 Angular 2。
app/pages/todos/home.ts

import { Component } from '@angular/core';
import { NavController } from 'ionic-angular';
import { AddPage } from "../add/add";
import { DetailPage } from "../detail/detail";

@Component({
  selector: 'page-home',
  templateUrl: 'home.html'
})
export class HomePage {
 
    public todoList: Array<any> = [];
 
    constructor(private navCtrl: NavController) { }
 
    ionViewDidEnter() {
        this.todoList = JSON.parse(localStorage.getItem("todos"));
        console.log(this.todoList);
    }
 
    delete(index: number) {
        this.todoList.splice(index, 1);
        localStorage.setItem("todos", JSON.stringify(this.todoList));
    }
 
    add() {
        this.navCtrl.push(AddPage);
    }

    detail(index: number){
        this.navCtrl.push(DetailPage, {
          id: index
        });
    }
 
}

AddPage DetailPage 等等處理。

我們在 ionViewDidEnter() 才導入 localStorage 資料而非 constructor() 是因為前者可以讓我們每次進入畫面都觸發,而後者只有在被導向進來時才觸發 (如果按返回就沒事)。
資料部分簡單用 localStorage 以 json 來做儲存。當然你也可以用 DB 外掛來處理。

this.navCtrl.push() 可以導向其他頁面。例如 this.navCtrl.push(AddPage)。也可以加上參數 this.navCtrl.push(DetailPage, {id: index});

增加事項的頁面 add

html

<ion-header>
    <ion-navbar>
        <ion-title>Add Item</ion-title>
        <ion-buttons end>
            <button (click)="save()" ion-button icon-only><ion-icon name="checkmark"></ion-icon></button>
        </ion-buttons>
    </ion-navbar>
</ion-header>
 
<ion-content>
    <ion-list>
        <ion-item>
            <ion-label floating>Title</ion-label>
            <ion-input type="text" [(ngModel)]="todoTitle"></ion-input>
        </ion-item>
        <ion-item>
            <ion-label floating>Description</ion-label>
            <ion-input type="text" [(ngModel)]="todoDes"></ion-input>
        </ion-item>
    </ion-list>
</ion-content>

floating 標籤可以讓文字飄上去。

ts

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

@Component({
  templateUrl: 'add.html'
})
export class AddPage {
 
    public todoList: Array<any>;
    public todoTitle: string;
    public todoDes: string;
 
    constructor(private navCtrl: NavController) {
        this.todoList = JSON.parse(localStorage.getItem("todos"));
        if(!this.todoList) {
            this.todoList = [];
        }
        this.todoTitle = "";
        this.todoDes = ""; // description
    }
 
    save() {
        if(this.todoTitle != "" && this.todoDes != "") {
            this.todoList.push(
              {
                title: this.todoTitle,
                des: this.todoDes
              } 
            );
            localStorage.setItem("todos", JSON.stringify(this.todoList));
            this.navCtrl.pop();
        } else if(this.todoTitle == "") {
            alert("No Title!"); 
        } else if(this.todoDes == ""){
            alert("No Description!");
        }
    }
 
}

todoTitle todoDes 和模板雙向綁定,按下 save() 會儲存進 todos

查看細節頁面 detail

html

<ion-header>
    <ion-navbar>
        <ion-title>Detail</ion-title>
    </ion-navbar>
    <ion-buttons end>
        <button (click)="close()" ion-button icon-only><ion-icon name="checkmark"></ion-icon></button>
    </ion-buttons>
</ion-header>

<ion-content padding>
    <ion-card>
        <ion-card-header>
            {{ title }}
        </ion-card-header>
        <ion-card-content>
            {{ description }}
        </ion-card-content>
    </ion-card>
</ion-content>
<ion-header>
    <ion-navbar>
        <ion-title>Detail</ion-title>
        <ion-buttons end>
            <button (click)="close()"><ion-icon name="close"></ion-icon></button>
        </ion-buttons>
    </ion-navbar>
</ion-header>

<ion-content padding>
    <ion-card>
        <ion-card-header>
            {{ title }}
        </ion-card-header>
        <ion-card-content>
            {{ description }}
        </ion-card-content>
    </ion-card>
</ion-content>

ts

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

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

  public title: string;
  public description: string;

  constructor(public navCtrl: NavController, public navParams: NavParams) {
    let id: number = navParams.get('id');
    let todoList: any = JSON.parse(localStorage.getItem("todos"));
    this.title = todoList[id].title;
    this.description = todoList[id].des;
    console.log(this.title);
  }

  ionViewDidLoad() {
    //console.log('ionViewDidLoad DetailPage');
  }

  close(){
    this.navCtrl.pop();
  }

}

還記得剛剛在 home.tsdetail(i),我們再導入進來 detail 時候有輸入參數嗎?
這時候我們要 import { NavParams } from 'ionic-angular'; 使用 NavParams,就可以取得參數 let id: number = navParams.get('id');

SCSS

接著我們稍微修飾一下我們的外觀
打開 src/theme/variables.scss
這邊的 scss 會是全域變數。

$toolbar-ios-title-font-size: 2.3rem;
$toolbar-ios-height: 30px;
$font-family-base: Microsoft JhengHei, -apple-system, "Helvetica Neue", "Roboto", sans-serif !default;

預設標題真的很小,把標題變大一點。

打開 src/pages/home/home.scss

.danger{
    background-color: #f53d3d;
    color: white;
}

我們讓刪除的按鈕變成紅色的。

大功告成囉!

下回介紹如何輸出,實際放到手機上!


上一篇
[Day 22] Angular 2 + Ionic = Mobile App ( 2 ) 建構
下一篇
[Day 24] Angular 2 + Ionic = Mobile App ( 4 ) 發布 App
系列文
Angular 2 之 30 天邁向神乎其技之路31

尚未有邦友留言

立即登入留言