ใน ionic จะมี Lifecycle events เกิดขึ้นในลักษณะหรือรูปแบบคล้ายๆ กับ Angular แต่อาจจะมี
ชื่อที่แตกต่างๆ ออกไป ซึ่งวัตถุประสงค์ก็จะไม่ต่างกัน Lifecycle event คือคำสั่งที่เราสามารถ
กำหนดเรียกใช้ให้ทำงานใดๆ ในช่วงระหว่างลำดับการทำงานที่ต้องการได้ สามารถอ่านทบทวนเกี่ยวกับ
การทำงานของ lifecycle ของ Angular เป็นแนวทางเพิ่มเติมได้ที่
ทำความรู้จักกับ และใช้งาน Lifecycle Hook ใน Angular http://niik.in/858
https://www.ninenik.com/content.php?arti_id=858 via @ninenik
ตามที่ได้กล่าวไปในหัวข้อ ว่าเราจะพูดถึง lifecycle event ที่เกิดขึ้นระหว่างการเปลี่ยนหน้า page หรือการ
แสดงเพิ่มเข้ามาและการปิดหรือลบออกไป ของ component ที่มีรูปแบบการใช้งานคล้าย page เช่น Tab ,
Popover , Modal เหล่านี้เป็นต้น ซึ่งในบางครั้ง เราต้องการกำหนดการทำงานบางอย่างแทรกเข้าไป เราจึง
มาทำความเข้าใจเกี่ยวกับการใช้งาน lifecycle event กัน โดยจะขออธิบายต่อจากบทความที่ผ่านมา ตามลิ้งค์
ด้านล่าง
แสดงข้อมูล Service API แบบ Cards และการส่งค่าระหว่าง Page ใน Ionic http://niik.in/868
https://www.ninenik.com/content.php?arti_id=868 via @ninenik
จากตอนที่แล้ว หน้าหลัก สมมติให้เป็น Page A จะเป็นหน้ารายการบทความ 10 รายการแสดงอยู่ ซึ่งเมื่อผู้ใช้กด
เลือกรายการใดๆ ใน Page A ก็จะแสดงหน้ารายละเอียด สมมติให้เป็น Page B โดยมีการส่งค่า id มายัง Page B
ด้วย โดยค่า id นั้นจะถูกนำไปเป็นเงื่อนไขในการดึงรายละเอียดของข้อมูลมาแสดง Page B จะซ้อนทับอยู่ด้านบนของ
Page A และ Page A ยังไม่ถูกลบออกไป เพียงแค่ซ่อนอยู่ด้านล่างใต้ Page B ที่ active อยู่
Lifecycle events
เรามาดูลำดับการทำงานของ lifecycle event ในการเปลี่ยนหน้า Page A ไป Page B และกรณีปิด Page B และแสดง
Page A
กรณีแรก Page A active อยู่ และเราเปิด Page B ซ้อนขึ้นมา ลำดับของ lifecycle ของ Page A และ Page B
จะเป็นดังนี้
1. Page B ทำการโหลด เกิด ionViewDidLoad [B] ซึ่ง ionViewDidLoad จะเกิดขึ้นเพียงครั้งเดียว เมื่อมีการสร้าง Page
เกืดขึ้นในครั้งแรก อย่างในกรณีนี้ Page B เพิ่งถูกสร้างและใช้งานครั้งแรก โดยปกติ จะใช้ส่วนของ event นี้ในการกำหนดค่าเริ่มต้นของ Page ที่จะแสดง]
2. ในขณะที่ Page B กำลังจะ Active แสดงขึ้นมา Page A ที่แสดงอยู่ด้านล่าง ก็จะเข้าสู่โหมด Cache และเกิด
ionViewWillLeave [A] โดย event นี้จะทำให้ Page A ออกจากโหมด Active หรือก็คือจะเข้าสู่โหมด Inactive แต่ยังไม่ได้ถูกลบออกไป
แค่จะซ่อนอยู่ด้านหลังของ Page B ที่กำลังจะ Active
แค่จะซ่อนอยู่ด้านหลังของ Page B ที่กำลังจะ Active
3. Page B หลังจากโหลดเรียบร้อยแล้ว Page B ก็จะเริ่มทำการ Active เกิด ionViewWillEnter [B] หรือก็คือ Page B
กำลังจะ Active แสดงขึ้นมานั่นเอง
4. Page B แสดงหรือ active เกิด ionViewDidEnter [B] โดย event นี้จะเกิดขึ้นทุกครั้งเมื่อ Page มีการ Active ทั้ง
กรณีจากการสร้าง Page ใหม่หรือกรณี Page ที่ทำการ Cache อยู่และกลับมา Active อีกครั้ง
5. Page A เข้าสู่โหมด Cache หรือ เข้าสู่โหมด Inactive เกิด ionViewDidLeave [A]
จะได้ลำดับของ Event กรณีแรกที่ Page B Active แสดงซ้อนด้านบน Page A ที่ Inactive เป็นดังนี้
- ionViewDidLoad [B]
- ionViewWillLeave [A]
- ionViewWillEnter [B]
- ionViewDidEnter [B]
- ionViewDidLeave [A]
ภาพประกอบลำดับการทำงานกรณีสร้าง Page ใหม่ ด้วย push() method
กรณีที่สอง Page B ที่ Active ซ้อน Page A ที่ Inactive ได้ถูกปิดออกไป และกลับมาแสดง Page A ลำดับ
ของ lifecycle จะเป็นดังนี้
1. Page A ที่อยู่ในโหมด Cache หรือ Inactive จะกลับมา Active อีกครั้ง เกิด ionViewWillEnter [A]
2. Page B ซึ่ง Active ซ้อนอยู่จะเข้าสู่โหมด Inactive เกิด ionViewWillLeave [B]
3. Page A กลับจากโหมด Cache หรือ Inactive เข้าสู่โหมด Active เกิด ionViewDidEnter [A] จะเห็นว่า
มีการข้าม ionViewDidLoad [A] หรือก็คือไม่เกิด ionViewDidLoad [A] ขึ้น เพราะ Page A กลับมาจากโหมด
Cache หรือโหมด Inactive ไม่ได้มีการสร้าง Page A ใหม่
4. Page B เข้าสู่โหมด Inactive แต่จะไม่มีการ Cache เกิด ionViewDidLeave [B]
5. Page B ถูกลบออกไป เกิด ionViewWillUnload [B]
จะได้ลำดับของ Event กรณีที่สอง Page A กลับมาจากแสดง และปิด Page B เป็นดังนี้
- ionViewWillEnter [A]
- ionViewWillLeave [B]
- ionViewDidEnter [A]
- ionViewDidLeave [B]
-
ionViewWillUnload [B]
ภาพประกอบลำดับการทำงานกรณีปิด Page ด้วย pop() method
Lifecycle method ข้างต้น จะมีประโยชน์สำหรับให้เราสามารถแทรกการทำงานในช่วงจังหวะที่ต้องการ ตาม
ความเหมาะสมได้ ยกตัวอย่างกรณีที่เห็นภาพได้ชัด เช่น สมมติเรามี Page อยู่ 3 Page เป็นหน้า ให้เป็น
- Page A หน้ารายการเพลง
- Page B หน้ากำลังเล่นเพลงที่เลือกจาก Page A
- Page C หน้ารายละเอียดข้อมูลเพลงที่กำลังเล่นอยู่
โดยการทำงานของ Page คือ ในขณะที่ Page A แสดงรายการอยู่ พอเราเลือกเพลงเพื่อที่จะเล่น Page B ก็จะซ้อน
เข้ามาอยู่ด้านบนของ Page A และเล่นเพลงที่เราเลือก และสมมติเราต้องการดูรายละเอียดของเพลงที่เล่นอยู่ใน Page B
เมื่อกดปุ่มใดๆ ที่จะแสดงรายละเอียด ก็จะแสดง Page C ขึ้นมาซ้อนทับ Page B อีกที ซึ่งลำดับขั้นตอนการแสดง
Page ข้างต้น ก็จะเกิด lifecycle event ของแต่ละ page แตกต่างกันไป
หากเราต้องการลำดับการทำงานเป็นดังนี้ เมื่อเปิด Page B ให้เล่นเพลงเลย และถ้า กดดูรายละเอียดของเพลง
เมื่อ Page C แสดง ก็ให้หลุดเพลงใน Page B ไว้ก่อน และเมื่อปิดรายละเอียดของเพลง หรือปิด Page C ไป ก็ให้
Page B กลับไปเล่นเพลงต่อ โดยเล่นต่อจากที่เล่นค้างไว้
เราสามารถกำหนดการทำงานเข้าไปใน lifecycle event ของ Page B เป็นดังนี้
ionViewDidLoad() เมื่อ Page B แสดงครั้งแรก ให้เรากำหนดการโหลดเพลง ชื่อเพลง หรือรายละเอียดต่างๆ
ของเพลงที่เราต้องการจะเล่น
ionViewWillEnter() เมื่อทุกครั้งที่ Page B กลับมา Active เราก็ให้ Play หรือเล่นเพลง ทั้งกรณีเปิดเพลง เมื่อเลือก
เพลงจาก Page A เป็นการเริ่มเล่นเพลง กับกรณีปิดหน้า Page C ซึ่งเป็นรายละเอียดของเพลงไป เป็นเล่นเพลงต่อ
ionViewDidLeave() เมื่อ Page B กำลังเข้าสู่โหมด Inactive เช่นกรณีจะแสดงรายละเอียดของเพลง เราก็ให้ทำการ Pause
หรือหยุดเล่นเพลงชั่วคราว
ionViewWillUnload() เมื่อ Page B จะปิดไปเพื่อกลับไปหน้ารายการเพลง เราก็ให้ทำการ Stop หรือหยุดเล่นเพลงนั้นๆ
และนี้คือรูปแบบการกำหนดการทำงานเข้าไปในช่วงจังหวะที่ต้องการใน Page B ด้วยการใช้งาน lifecycle event
สรุป lifecycle event เบื้องต้น
ionViewDidLoad
เกิดครั้งแรกครั้งเดียวเมื่อเริ่มมีการสร้าง Page เหมาะสำหรับใช้ในการกำหนดค่าต่างๆ ที่จะใช้งานใน Page
ionViewWillEnter
เกิดขึ้นทุกๆ ครั้งที่ Page กำลังจะ Active ทั้งจากกรณีเริ่มต้นเมื่อสร้าง Page หรือกลับมาจาก Inactive
ionViewDidEnter
เกิดขึ้นทุกๆ ครั้งที่ Page มีการ Active ทั้งจากกรณีเริ่มต้นเมื่อสร้าง Page หรือกลับมาจาก Inactive
ionViewWillLeave
เกิดขึ้นทุกๆ ครั้งที่ Page กำลังจะเข้าสู่โหมด Cache หรือ Inactive
ionViewDidLeave
เกิดขึ้นทุกๆ ครั้งที่ Page เข้าสู่โหมด Cache หรือ Inactive
ionViewWillUnload
เกิดขึ้นทุกๆ ครั้งเมื่อมีการปิด Page นั้นด้วยคำสั่ง pop() เพื่อลบ Page นั้นออกไป
Nav Guards
นอกจาก lifecycle event ที่ใช้สำหรับกำหนดการทำงานในช่วงจังหวะใดๆ ของ Page ทั้ง 6 รายการข้างต้นแล้ว
เรายังมีอีก 2 lifecycle event ที่ใช้ในการควบคุมการอนุญาตที่จะให้เข้าไปยังหน้า Page ใดๆ กับการควบคุมการอนุญาต
ในการที่จะออกจาก Page ใดๆ คือ
ionViewCanEnter
เกิดขึ้นก่อนที่จะมีการโหลด Page ใดๆ ใช้สำหรับการกำหนดสิทธิการเข้าไปยังหน้า Page เช่น ใช้ในการตรวจสอบว่า
สามารถเข้าไปใช้งานหรือแสดงหน้า Page ที่ต้องการหรือไม่ โดยจะมีการคืนค่าเป็น true ถ้าได้สิทธิ์เข้าไปยัง Page นั้นๆ
และจะคืนค่าเป็น false ถ้าไม่อนุญาตให้เข้ายังหน้า Page นั้น
ionViewCanLeave
เกิดขึ้นก่อนที่จะมีการออกจาก Page ใดๆ ใช้สำหรับกำหนดเงื่อนไขในการออกจาก Page เช่น สมมติ Page นั้นมีการแก้ไข
ข้อมูล และต้องการให้ผู้ใช้ทำการยืนยันการเปลี่ยนแปลงข้อมูลก่อนออกจาก Page โดยให้ทำการบันทึกหรือยกเลิกการแก้ไข
ก่อนที่จะออกจาก Page โดยจะมีการคืนค่าเป็น true ถ้าอนุญาตให้ออกจาก Page นั้นๆ และจะคืนค่าเป็น false ถ้าไม่อนุญาตให้ออกจาก Page นั้น
เรามาลองทดสอบลำดับการทำงานของ lifecycle event ทั้งหมด ประยุกต์เข้ากับเนื้อหาบทความในตอนที่แล้ว ที่เป็น
การแสดงรายการเนื้อหาจากเว็บไซต์ โดยให้ article.ts เป็นส่วนของไฟล์การทำงานใน Page A และ article-detail.ts
เป็นส่วนของไฟล์การทำงานใน Page B และแทรก console ข้อความระบุขั้นตอนในการทำงานลงไป เป็นดังนี้
ไฟล์ article.ts (Page A)
import { Component } from '@angular/core'; import { NavController, NavParams, LoadingController } from 'ionic-angular'; import { ArticleServiceProvider } from '../../providers/article-service/article-service'; import { ArticleDetailPage } from '../article-detail/article-detail'; interface articles { id: number, topic: string, description: string, date: string, img: string, view: number } @Component({ selector: 'page-article', templateUrl: 'article.html', }) export class ArticlePage { public articleItems: articles; public pushPage:any; constructor( public navCtrl: NavController, public navParams: NavParams, public articleService: ArticleServiceProvider, public loadingCtrl: LoadingController ) { this.pushPage = ArticleDetailPage; } ionViewCanEnter(){ console.log("Page A CanEnter"); return true; } ionViewDidLoad() { console.log("Page A DidLoad"); let loading = this.loadingCtrl.create({ content: 'Loading ...' }); loading.present(); this.articleService.getArticle() .subscribe((res: articles) => { this.articleItems = res; loading.dismiss(); }); } ionViewWillEnter(){ console.log("Page A WillEnter"); } ionViewDidEnter(){ console.log("Page A DidEnter"); } ionViewWillLeave(){ console.log("Page A WillLeave"); } ionViewDidLeave(){ console.log("Page A DidLeave"); } ionViewWillUnload(){ console.log("Page A WillUnload"); } ionViewCanLeave(){ console.log("Page A CanLeave"); return true; } openArticle(id: any) { this.navCtrl.push(ArticleDetailPage, { id: id }); } }
ไฟล์ article-detail.ts (Page B)
import { Component } from '@angular/core'; import { NavController, NavParams, LoadingController } from 'ionic-angular'; import { ArticleServiceProvider } from '../../providers/article-service/article-service'; interface articles{ id:number, topic:string, description:string, date:string, img:string, view:number } @Component({ selector: 'page-article-detail', templateUrl: 'article-detail.html', }) export class ArticleDetailPage { public article:articles; constructor( public navCtrl: NavController, public navParams: NavParams, public articleService:ArticleServiceProvider, public loadingCtrl: LoadingController ){ } ionViewCanEnter(){ console.log("Page B CanEnter"); return true; } ionViewDidLoad() { console.log("Page B DidLoad"); let loading = this.loadingCtrl.create({ spinner:'ios', content: 'Loading ...' }); loading.present(); let id = this.navParams.get("id"); this.articleService.getArticle(id) .subscribe((res:articles) =>{ this.article = res[0]; loading.dismiss(); }); } ionViewWillEnter(){ console.log("Page B WillEnter"); } ionViewDidEnter(){ console.log("Page B DidEnter"); } ionViewWillLeave(){ console.log("Page B WillLeave"); } ionViewDidLeave(){ console.log("Page B DidLeave"); } ionViewWillUnload(){ console.log("Page B WillUnload"); } ionViewCanLeave(){ console.log("Page B CanLeave"); return true; } }
ลำดับการทำงานเมื่อเปลี่ยนจาก Page A ไป Page B
ลำดับการทำงานเมื่อเปลี่ยนกลับจาก Page B กลับมา Page A
การใช้งาน lifecycle event ไม่จำเป็นที่เราต้องเรียกใช้งานหรือกำหนดใช้งานทุก event เราสามารถเลือกใช้ event
ตามความเหมาะสม ในตัวอย่างทดลองเรียกใช้เพื่อให้เห็นผลลัพธ์ของการทำงาน ผ่านการแสดงค่าทาง console
สำหรับ ionViewCanEnter และ ionViewCanLeave นั้น ในตัวอย่างเราให้คืนค่าเป็น true เพื่อทดสอบเท่านั้น ในการ
นำไปประยุกต์ใช้งาน เราสามารถสร้าง service หรือเงื่อนไขการคืนค่าตามต้องการได้ เช่น ตรวจสอบว่า ผู้ใช้ทำการล็อกอิน
เข้าสู่ระบบแล้วหรือไม่ ถ้ายังไม่ได้ทำการล็อกอิน ก็ return false เพื่อไม่ให้ผู้ใช้เรียกแสดงหน้า Page นั้นๆ ได้
สำหรับเนื้อหาเกี่ยวกับ lifecycle event ก็ขอจบแนวทางเพียงเท่านี้ เนื้อหาต่อไปจะเป็นอะไร รอติดตาม