เนื้อหานี้ต่อจากตอนที่แล้ว
ศึกษาและใช้งาน ionic material จาก demo เบื้องต้น
https://www.ninenik.com/content.php?arti_id=696 via @ninenik
จริงๆ แล้วเนื้อหาส่วนนี้จะมีอยู่ในบทความเก่า เกี่ยวกับ navigation และ template
การใช้งาน navigation ใน ionicframework ตอนที่ 5
https://www.ninenik.com/content.php?arti_id=541 via @ninenik
สามารถย้อนกลับไปศึกษาเพิ่มเติมได้ แต่ในที่นี้
เราจะทำการศึกษาาจากไฟล์ demo ว่ามีการทำงาน การประยุกต์ และมีรูปแบบที่น่า
เอามาต่อยอดได้เป็นอย่างไรบ้าง
มาดูโครงสร้างของ demo จากเนื้อหาตอนที่แล้ว

จากรูป เราจะสนใจในส่วนของไฟล์ app.js controller.js
และก็ไฟล์ในส่วนของ template
ไฟล์ app.js ตัด comment ออก
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 | angular.module( 'starter' , [ 'ionic' , 'starter.controllers' , 'ionic-material' , 'ionMdInput' ]) .run( function ($ionicPlatform) { $ionicPlatform.ready( function () { if (window.cordova && window.cordova.plugins.Keyboard) { cordova.plugins.Keyboard.hideKeyboardAccessoryBar( true ); } if (window.StatusBar) { StatusBar.styleDefault(); } }); }) .config( function ($stateProvider, $urlRouterProvider, $ionicConfigProvider) { $ionicConfigProvider.views.maxCache(0); $stateProvider.state( 'app' , { url: '/app' , abstract: true , templateUrl: 'templates/menu.html' , controller: 'AppCtrl' }) .state( 'app.activity' , { url: '/activity' , views: { 'menuContent' : { templateUrl: 'templates/activity.html' , controller: 'ActivityCtrl' }, 'fabContent' : { template: '<button id="fab-activity" class="button button-fab button-fab-top-right expanded button-energized-900 flap"><i class="icon ion-paper-airplane"></i></button>' , controller: function ($timeout) { $timeout( function () { document.getElementById( 'fab-activity' ).classList.toggle( 'on' ); }, 200); } } } }) .state( 'app.friends' , { url: '/friends' , views: { 'menuContent' : { templateUrl: 'templates/friends.html' , controller: 'FriendsCtrl' }, 'fabContent' : { template: '<button id="fab-friends" class="button button-fab button-fab-top-left expanded button-energized-900 spin"><i class="icon ion-chatbubbles"></i></button>' , controller: function ($timeout) { $timeout( function () { document.getElementById( 'fab-friends' ).classList.toggle( 'on' ); }, 900); } } } }) .state( 'app.gallery' , { url: '/gallery' , views: { 'menuContent' : { templateUrl: 'templates/gallery.html' , controller: 'GalleryCtrl' }, 'fabContent' : { template: '<button id="fab-gallery" class="button button-fab button-fab-top-right expanded button-energized-900 drop"><i class="icon ion-heart"></i></button>' , controller: function ($timeout) { $timeout( function () { document.getElementById( 'fab-gallery' ).classList.toggle( 'on' ); }, 600); } } } }) .state( 'app.login' , { url: '/login' , views: { 'menuContent' : { templateUrl: 'templates/login.html' , controller: 'LoginCtrl' }, 'fabContent' : { template: '' } } }) .state( 'app.profile' , { url: '/profile' , views: { 'menuContent' : { templateUrl: 'templates/profile.html' , controller: 'ProfileCtrl' }, 'fabContent' : { template: '<button id="fab-profile" class="button button-fab button-fab-bottom-right button-energized-900"><i class="icon ion-plus"></i></button>' , controller: function ($timeout) { } } } }) ; $urlRouterProvider.otherwise( '/app/login' ); }); |
จะอธิบายและศึกษาเฉพาะส่วน หากใครเริ่มต้นศึกษา แนะนำให้อ่านบทความเกี่ยวกับ ionicframework
ในตอนต้นๆ ก่อนเพื่อความเข้าใจ
1 | angular.module( 'starter' , [ 'ionic' , 'starter.controllers' , 'ionic-material' , 'ionMdInput' ]) |
โค้ดส่วนนี้ เป็นการลงทะเบียนและเรียกใช้ angular modules
starter คือ ชื่อ module ที่เรากำหนดในไฟล์ index.html
1 2 3 | < body ng-app = "starter" > < ion-nav-view ></ ion-nav-view > </ body > |
สวน starter.controllers คือ ชื่อ module ที่เรียกใช้จากไฟล์ controllers.js
1 2 3 4 5 | /* global angular, document, window */ 'use strict' ; angular.module( 'starter.controllers' , []) .......... |
ส่วนค่าอื่นๆ ที่เหลือก็เป็นชื่อ module ที่เรามีการใช้งานร่วมกับ app ของเรา
ได้แก่ ionic , ionic-material , ionMdInput
โค้ดส่วนต่อมา
1 2 3 4 5 6 7 8 9 10 | .run( function ($ionicPlatform) { $ionicPlatform.ready( function () { if (window.cordova && window.cordova.plugins.Keyboard) { cordova.plugins.Keyboard.hideKeyboardAccessoryBar( true ); } if (window.StatusBar) { StatusBar.styleDefault(); } }); }) |
โค้ดส่วนนี้ให้ข้ามไปก่อน เรายังไม่สนใจ
ส่วนที่เราให้ความสนใจ และต้องการศึกษาในตอนนี้คือส่วนของ
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | .config( function ($stateProvider, $urlRouterProvider, $ionicConfigProvider) { $ionicConfigProvider.views.maxCache(0); $stateProvider.state( 'app' , { url: '/app' , abstract: true , templateUrl: 'templates/menu.html' , controller: 'AppCtrl' }) .state( 'app.activity' , { url: '/activity' , views: { 'menuContent' : { templateUrl: 'templates/activity.html' , controller: 'ActivityCtrl' }, 'fabContent' : { ......... .... .. $urlRouterProvider.otherwise( '/app/login' ); }); |
สำหรับโค้ดในส่วนนี้ เหมือนการวางโครงสร้างว่า app ของเราจะมีหน้าไหนบ้าง
state ก็เหมือนส่วนของตำแหน่งของการแสดง app ของเรา เช่น
state เริ่มต้นชื่อว่า app มื child state เป้น app.activity และอื่นๆ
เรามาดูว่าโค้ดตัวอย่างของเรามี state อะไรบ้าง
- ส่วนแรกสุดเป็น parent state ชื่อ ว่า app
- ส่วนทีสองเป็น child state ได้แก่ activity ,friends, gallery, login, profile
เวลากำหนดเราจะใช้รูปแบบ parent.child ตัวอย่างถ้าเป็น state activity
ก็จะกำหนดได้เป้น app.activity แบบนี้เป็นต้น
ส่วนต่อไปเรามาดู url หรือก็ส่วนที่แสดงบน address bar ของแต่ละ state
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | $stateProvider.state( 'app' , { url: '/app' , abstract: true , templateUrl: 'templates/menu.html' , controller: 'AppCtrl' }) .state( 'app.activity' , { url: '/activity' , }) .state( 'app.friends' , { url: '/friends' , }) .state( 'app.gallery' , { url: '/gallery' , }) .state( 'app.login' , { url: '/login' , }) .state( 'app.profile' , { url: '/profile' , }) ; $urlRouterProvider.otherwise( '/app/login' ); |
รูปแบบก็จะเป้น '/ชื่อ state' เช่นถ้าเราเปิดหน้า app ไปที่หน้า profile
url ที่แสดงบน address bar ก็จะเป้น #/app/profile
สังเกตว่า prfile จะเป็น child state ของ app ที่เป็น parent state
url ของ app ที่เป็น parent state คือ '/app'
url ของ profile ที่เป็น child state คือ '/profile'
url ของ address bar ของ app.profile state ก็จะได้เป้น #/app/profile แบบนี้เป็นต้น
(# คือรูปแบบการจัดการ url ของ hybrid app ซึ่งจะต่างกับรูปแบบเว็บทั่วไป ต่อไปจะขอละ # มีพิมพ์แสดง
ให้เข้าใจว่าจะมี # นำหน้าเสมอ)
ต่อมา มาดูในส่วนของ parent state
1 2 3 4 5 6 | $stateProvider.state( 'app' , { url: '/app' , abstract: true , templateUrl: 'templates/menu.html' , controller: 'AppCtrl' }) |
จะมีส่วนของการกำหนด abstract ,templateUrl: และ controller
abstract: true, ก็คือกำหนดให้ parent state นี้มีสถานะเป็น abstract state
Abstract state นี้จะไม่ถูกใช้งานหรือ activated ด้วมันเอง ซึ่งก็คือ เราไม่สามารถเรียกใช้งาน
ผ่าน /app ปกติเพื่อให้ state นี้ทำงานได้ แต่ state นี้จะถูกใช้งานอัตโนมัติเมื่อ child state ถูกเรียกใช้
เช่นเมื่อเรียกใช้งาน /app/profile ซึ่งเป็น child state ดังนี้แล้ว app ชื่อเป็น parent state ก็จะถูก
เรียกใช้งานในทางอ้อมไปด้วย โดยมีการเรียกไฟล์ template มาใช้งานและสร้าง ชื่อ controller
ดังนั้น เมื่อเรามีการเรียกใช้งาน /app/profile
ก็จะไปดึงรูปแบบการแสดงผลจากไฟล์ menu.html ผ่าน templateUrl
1 | templateUrl: 'templates/menu.html' , |
และก็มีการสร้าง controller ชื่อ AppCtrl ขึ้นผ่าน
1 | controller: 'AppCtrl' |
ต่อไปเรามาดูส่วนของ child state จะสังเกตเห็นว่า รูปแบบการใช้งานก็จะคล้ายกัน
เปลี่ยนค่าไปตามส่วนที่กำหนด ขอยกมาสัก state มาอธิบาย
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | .state( 'app.activity' , { url: '/activity' , views: { 'menuContent' : { templateUrl: 'templates/activity.html' , controller: 'ActivityCtrl' }, 'fabContent' : { template: '<button id="fab-activity" class="button button-fab button-fab-top-right expanded button-energized-900 flap"><i class="icon ion-paper-airplane"></i></button>' , controller: function ($timeout) { $timeout( function () { document.getElementById( 'fab-activity' ).classList.toggle( 'on' ); }, 200); } } } }) |
แยกมาดูส่วนของ views
1 2 3 4 5 6 7 8 9 10 11 | .state( 'app.activity' , { url: '/activity' , views: { 'menuContent' : { }, 'fabContent' : { } } }) |
views ของแต่ละ state ก็คือ ส่วนของการแสดงข้อมูลซึ่งเราสามารถแยกย่อยไปแต่ละส่วนได้
เรากลับไปดูที่ไฟล์ menu.html ที่เป็น template แรกที่ถูกโหลดมาใช้งาน โดยดูโครงสร้าง app ประกอบ
ตามรูป


ดูโค้ดไฟล์ menu.html ประกอบ
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | < ion-side-menus enable-menu-with-back-views = "true" > < ion-side-menu-content > < ion-nav-bar class = "bar-assertive-900" ng-class = "{expanded: isExpanded, 'has-header-fab-left': hasHeaderFabLeft, 'has-header-fab-right': hasHeaderFabRight}" align-title = "left" > <!-- ส่วนหัวของ app หรือส่วนของ bar จากรูปด้านบนก็จะเป้นส่วนขอกรอบงสี่เหลี่ยมสีเหลือง--> </ ion-nav-bar > < ion-nav-view name = "fabContent" > <!-- ส่วนของไอค่อนเมนูย่อย ส่วนของกรอบสีเขียวและฟ้า--> </ ion-nav-view > < ion-nav-view name = "menuContent" ng-class = "{expanded: isExpanded}" > <!-- เนื้อหาของ app จะแสดงในส่วนนี้ ส่วนของกรอบสีม่วง--> </ ion-nav-view > </ ion-side-menu-content > < ion-side-menu side = "right" > <!-- ส่่วนของ เมนูด้านขวา ส่วนของกรอบสีชมพู --> < ion-header-bar class = "dark-bg expanded" > <!-- ส่วนหัวหรือ bar ของเมนูด้านขวา--> </ ion-header-bar > < ion-content class = "stable-bg has-expanded-header" > <!-- เนื้อหาของเมนูด้านขวา--> </ ion-content > </ ion-side-menu > </ ion-side-menus > |
รูปแบบของ app demo จะเป็นแบบเมีเมนูด้านขวา ใช้งาน ion-side-menu
และมีแท็บ subheader พร้อมปุ่มไอคอน ขอแยกให้ดูง่ายขึ้น
1 2 3 4 5 6 7 8 | < ion-side-menus enable-menu-with-back-views = "true" > < ion-side-menu-content > <!-- เนื้อหาตรงกลางของ app กรอบสีม่วง --> </ ion-side-menu-content > < ion-side-menu side = "right" > <!-- ส่่วนของ เมนูด้านขวา กรอบสีชมพู--> <!-- เนื้อหาของเมนูด้านขวา--> </ ion-side-menu > </ ion-side-menus > |
ขอจบส่วนของตอนนี้ไว้แค่นี้ก่อน ดูต่อในเนื้อหาตอนหน้า