Ionic Tutorial

Lesson 7: DAO設計模式

Outline

  • Services & Models:資料存取物件 (DAO) 設計模式
  • Promise
  • Observable: (Pub-Sub)設計模式

資料存取物件設計模式

Data Access Object (DAO) design pattern

Icons made by monkik from  www.flaticon.com is licensed by CC 3.0 BY

Icons made by Freepik from  www.flaticon.com is licensed by CC 3.0 BY

軟體解決方案

問題(經常發生)

得到解決

設計模式

資料存取設計模式

資料模型TS

資料介面TS

資料服務

資料庫

實作

TS檔

使用

schema

對應

存取

元件

TS檔

HTML檔

屬性繫結

事件繫結

注入

呼叫服務

資料存取設計模式

DAO實作: 建立與使用services

資料模型TS

資料介面TS

資料服務

資料庫

實作

TS檔

使用

schema

對應

存取

元件

TS檔

HTML檔

屬性繫結

事件繫結

注入

呼叫服務

Ⓐ 新增_model資料夾
新增「資料模型.ts檔」

Ⓑ 在_model資料夾中
新增「資料界面.ts檔」

Ⓒ 建立服務

ionic g service 服務名稱

實作資料存取

Ⓓ 使用服務

DAO實作

Check List

步驟 範例
Ⓐ 建立資料模型 models/place.ts
Ⓑ 建立資料介面 models/placeDAO.ts
Ⓒ 建立服務 ionic g service services/place
Ⓒ 撰寫服務(實作資料介面) services/place.service.ts
Ⓓ 使用服務 撰寫頁面

DAO實作

export interface Place {
    id: string;         // id
    title: string;      // 景點名稱
    address?: string;   // 地址
    location: {
        lat: number;    // 緯度
        lng: number;    // 經度
    };
    intro?: string;     // 景點介紹
}

Ⓐ 資料模型

「景點資料」需要哪些欄位?

 

以介面 (interface) 方式定義之

models/place.ts

DAO實作

import { Observable } from 'rxjs';
import { Place, PlaceID } from './place';

export interface PlaceDAO {
    // methods: 必須提供的功能
    createPlace(p: Place): Promise<any>;
    removePlace(id: string): Promise<any>;
    updatePlace(id: string, data: any): Promise<any>;
    queryPlaceById(id: string): Observable<Place> ; // 回傳Place型別的資料
    getAllPlaces(): Observable<PlaceID[]>;    // 回傳所有景點
}

「景點資料」需要哪些屬性?哪些操作方法?

以介面 (interface) 方式定義之

Ⓑ 資料界面

models/placeDAO.ts

DAO實作

import { Injectable } from '@angular/core';
// ...[省略]...
export class PlaceService implements PlaceDAO {
  // 第一部分:表單與欄位設定
  form: any; // 景點表單:暫存表單資料用

  constructor(private fs: AngularFirestore, private builder: FormBuilder) {
      this.form = this.builder.group({/*設定「新增/修改」表單所需欄位*/});
  }
  // 第一部分結束 ---------------------
  // 第二部分:  提供的API ------
  createPlace(p: Place): Promise<any> {
    /* 新增功能 */
  }
  removePlace(id: string): Promise<any> {
    /* 移除功能 */
  }
  updatePlace(id: string, data: any): Promise<any> {
    /* 修改功能 */
  }
  queryPlaceById(id: string): Observable<Place> {
    /* 查詢一筆資料 */
  }
  getAllPlaces(): Observable<PlaceID[]> {
    /* 查詢全部資料 */
  }
}

Ⓒ 建立服務

ionic g service services/place

建立服務

services/place.service.ts架構

import { Injectable } from '@angular/core';
import { AngularFirestore } from '@angular/fire/firestore';
import { FormBuilder } from '@angular/forms';
import { Observable } from 'rxjs';
import { Place, PlaceID } from '../models/place';
import { PlaceDAO } from '../models/placeDAO';

@Injectable({
  providedIn: 'root'
})
export class PlaceService implements PlaceDAO {
  form: any; // 景點表單:4個欄位

  constructor(private fs: AngularFirestore, private builder: FormBuilder) {
    this.form = this.builder.group({
      pid: ['', []], title: ['', []], address: ['', []], intro: ['', []]
    });
  }

  createPlace(p: Place): Promise<any> {
    return this.fs.collection('place').add(p);
  }
  removePlace(id: string): Promise<any> {
    return this.fs.collection('place').doc(id).delete();
  }
  updatePlace(id: string, data: any): Promise<any> {
    return this.fs.collection('place').doc(id)
      .set(data, { merge: true });
  }
  queryPlaceById(id: string): Observable<Place> {
    return this.fs.doc<Place>(`place/${id}`).valueChanges();
  }
  getAllPlaces(): Observable<PlaceID[]> {
    const dref = this.fs.collection<PlaceID>('place', ref => ref.orderBy('title'));
    return dref.valueChanges({ idField: 'id' });
  }
}

Ⓒ 建立服務

services/place.service.ts(完整程式碼)

注意:firebase後台需建立名為'place'的集合

DAO實作

...
import { PlaceService } from '../_service/place.service';
...
export class DetailPage implements OnInit {
  places: Place[];

  constructor(private ps: PlaceService) {
    this.places = this.ps.getAllPlaces();
  }
  ...
  goToPlace(id: stirng) {
    const place = this.ps.queryPlaceById(id);
    // ... 
  }
}

Ⓓ 使用服務

非同步執行:Promise

  • 什麼是Promise?

什麼是Promise? (1/4)

Consumer

不需停下等待

app端

提出要求

承諾

提供服務

服務端

什麼是Promise? (2/4)

Consumer

不需停下等待

app端

提出要求

承諾

提供服務

服務端

提供失敗

繼續執行

???

什麼是Promise? (3/4)

 回傳Promise

signInWithEmailAndPassword(e, p).then(/*回呼函式*/).catch(/*錯誤處理函式*/);
signInWithEmailAndPassword(e, p)
.then(
    (/*參數*/) => { /* 函式主體 */}
)
.catch(
    (/*參數*/) => { /* 函式主體 */}
);

什麼是Promise? (4/4)

signIn(user) {
    return this.afAuth.auth.signInWithEmailAndPassword(user.email, user.password)
    .then( 
      (credential) => {
          console.log('登入成功');        
          this.updateUserData(credential.user, credential.user.displayName);
      }
    )
    .catch(
      (error) => {
      console.log('登入失敗', error);
    });
}

Observable

出版-訂閱 設計模式 Publisher-Subscriber Design Pattern

訂閱-出版設計模式

❷ 訂閱

資料陸續產生publish

通知

 Observable

訂閱-出版設計模式

❷ Observer訂閱

資料陸續產生

通知

 Observable建立起來

下一筆資料

發生錯誤

結束

Observer

Observable範例

DAO設計模式

By Leuo-Hong Wang

DAO設計模式

  • 791