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設計模式
- 779