เนื้อหาต่อไปนี้เป็นแนวทางสำหรับการสร้างหน้า app เพื่อใช้ในการตั้งค่าต่างๆ ในระบบ
app ของเรา ในที่นี้จะแสดงเฉพาะตัวอย่างการตั้งค่าการรับข้อความ push เป็นเนื้อหาที่เกี่ยว
เนื่องกับตอนที่แล้ว
ประยุกต์ใช้งาน Push Notification plugin ใน ionic metarial
https://www.ninenik.com/content.php?arti_id=749 via @ninenik
จากบทความที่ผ่านมา เราได้รู้โครงสร้างของ database บนฝั่ง server ว่ามีการเก็บฟิลด์
notify_status เก็บเป็นค่า 0 กับ 1
โดย 0 ให้หมายถึงผู้ใช้ app ไม่ต้องการรับข้อความ push และ 1 คือผู้ใช้ต้องการรับข้อความ push
เงื่อนไขนี้ ไว้ใช้สำหรับตอนที่ทำการดึงข้อมูลผู้ใช้ที่ต้องการรับข้อความ push โดยเพิ่มเงื่อนไข
WHERE notify_status = 1 เข้าไปในคำสั่ง SQL ตอนดึงข้อมูลฝั่ง server
ดังนั้นหน้าตั้งค่าการเปิดปิด push notification เราก็จะทำการส่งค่า 0 และ 1 มายัง server เพื่ออัพเดท
ฟิลด์ notify_status นอกจากนั้นเรายังจำเป็นจะต้องเก็บค่าสถานะการเปิดปิด push notification ไว้
ที่เครื่องด้วย เพื่อเป็นค่าเริ่มต้นในการแสดง และไม่ต้องเช็คไปยัง server ตอนเปิด app ขึ้นมา โดยการ
บันทึกไว้ที่เครื่องเราจะจัดเก็บใน database ผ่าน SQLite เก็บไว้ในตาราง setting
เนื้อหานี้จะเป็นการอธิบายโค้ด โดยโค้ดที่ทำรองรับไว้แล้ว สามารถดาวน์โหลดไฟล์ได้ที่บทความ
แจกตัวอย่างโค้ด ionic material รองรับการตั้งค่า Push Notifcation
https://www.ninenik.com/content.php?arti_id=750 via @ninenik
1. สร้าง Router กำหนด state ให้กับหน้า setting
เราได้รู้มาแล้วจากบทความก่อนๆ ว่าเมื่อมีการสร้างหน้า app ขึ้นมาใหม่จะมีการกำหนด state
ของหน้านั้นๆ ศึกษาเพิ่มเติมได้ที่
แนวทางการเพิ่มหน้า app และการจัด layout ใน ionic material
https://www.ninenik.com/content.php?arti_id=733 via @ninenik
ในส่วนนี้ให้เราเปิดไฟล์ app.js ในโฟลเดอร์ js ดูในส่วนของ method config()
เราได้มีการเพิ่ม state ของหน้า setting ดังนี้
.state('app.settings', {// กำหนด state url: '/settings', // กำหนดตำแหน่งหน้า app คล้ายๆ url ในเว็บ views: { 'menuContent': { // ส่วนของการแสดงใน view ชื่อ menuContent templateUrl: 'templates/settings.html', // ใช้ template ชื่อ settings.html controller: 'SettingCtrl'// กำหนดชื่อ controller ที่จะใช้งาน }, 'fabContent': { // ส่วนของการแสดงใน view ชื่อ fabContent ไม่ได้กำหนดอะไร template: '' } } })
จากรูปแบบการกำหนด state ให้กับหน้า setting สิ่งที่เราจะมีเพิ่มเข้ามาคือไฟล์ settings.html
และส่วนของการควบคุมหน้า setting ด้วยการกำหนด controller ที่ชื่อ SettingCtrl
โดยจะอธิบายสองส่วนนี้ใน ข้อที่ 3 และ 4 ตามลำดับ
2. เพิ่มฟิลด์ notify_status ใน database ของ SQLite
เนื่องจากเราจำเป็นต้องทำการเก็บค่า สถานะการรับข้อความ push ไว้ที่เครื่อง เราจะทำการ
เพิ่มฟิลด์ notify_status เข้าไปในตาราง (ปล. กรณีแก้ไขตาราง เวลาทดสอบ app ให้ทำการลบ
การติดตั้ง app เดิมก่อนเพื่อให้ระบบ reset โครงสร้างตารางใหม่ตอนติดตั้ง)
ให้ดูในส่วนของ AppCtrl ส่วนของ controller หลัก
โดยเปิดไฟล์ controllers.js ในโฟลเดอร์ js
.controller('AppCtrl', function($scope, $ionicModal, $ionicPopover, $timeout, $ionicPopup,$ionicPlatform, $cordovaDialogs, $cordovaToast, $state ) { $scope.loginData = {}; $scope.isExpanded = false; $scope.hasHeaderFabLeft = false; $scope.hasHeaderFabRight = false; var navIcons = document.getElementsByClassName('ion-navicon'); for (var i = 0; i < navIcons.length; i++) { navIcons.addEventListener('click', function() { this.classList.toggle('active'); }); } //////////////////////////////////////// // Layout Methods //////////////////////////////////////// $scope.hideNavBar = function() { document.getElementsByTagName('ion-nav-bar')[0].style.display = 'none'; }; $scope.showNavBar = function() { document.getElementsByTagName('ion-nav-bar')[0].style.display = 'block'; }; $scope.noHeader = function() { var content = document.getElementsByTagName('ion-content'); for (var i = 0; i < content.length; i++) { if (content[i].classList.contains('has-header')) { content[i].classList.toggle('has-header'); } } }; $scope.setExpanded = function(bool) { $scope.isExpanded = bool; }; $scope.setHeaderFab = function(location) { var hasHeaderFabLeft = false; var hasHeaderFabRight = false; switch (location) { case 'left': hasHeaderFabLeft = true; break; case 'right': hasHeaderFabRight = true; break; } $scope.hasHeaderFabLeft = hasHeaderFabLeft; $scope.hasHeaderFabRight = hasHeaderFabRight; }; $scope.hasHeader = function() { var content = document.getElementsByTagName('ion-content'); for (var i = 0; i < content.length; i++) { if (!content[i].classList.contains('has-header')) { content[i].classList.toggle('has-header'); } } }; $scope.hideHeader = function() { $scope.hideNavBar(); $scope.noHeader(); }; $scope.showHeader = function() { $scope.showNavBar(); $scope.hasHeader(); }; $scope.clearFabs = function() { var fabs = document.getElementsByClassName('button-fab'); if (fabs.length && fabs.length > 1) { fabs[0].remove(); } }; $scope.noShadow = function() { var headerBar = document.getElementsByTagName('ion-header-bar'); for (var i = 0; i < headerBar.length; i++) { if (!headerBar[i].classList.contains('no-shadow')) { headerBar[i].classList.add('no-shadow'); } } }; $scope.hasShadow = function() { var headerBar = document.getElementsByTagName('ion-header-bar'); for (var i = 0; i < headerBar.length; i++) { if (headerBar[i].classList.contains('no-shadow')) { headerBar[i].classList.remove('no-shadow'); } } }; // ตัวแปรสำหรับกำหนด การซ่อนหรือแสดงเมนูสมาชิก false คือซ่อนเมนูสมาชิก $scope.showMemberMenu = false; // สร้างฟังก์ชั่น สำหรับเรียกใช้ เพื่อกำหนดการ กำหนดค่าตัวแปรเพื่อซ่อนหรือแสดงเมนู $scope.setMemberMenu = function(status){ $scope.showMemberMenu = status; }; // กำหนดตัวแปรไว้สำหรับเก็บ id ของสมาชิกที่สมัครในเครื่องนั้นๆ $scope.sesMemberID = null; // สร้างฟังก์ชั่นสำหรับกำหนดค่า id ให้สามารถเรียกใช้งานจาก $scope หลักได้ $scope.setMemberID = function(memID){ $scope.sesMemberID = memID; }; // สร้างฟังก์ชั่นสำหรับเรียกดูค่า id ของสมาชิกจาก $scope หลักได้ $scope.getMemberID = function(){ return $scope.sesMemberID; }; // สร้างฟังก์ชั่นสำหรับเรียกใช้ Toast plugin $scope.showToast = function(str, duration, position){ $ionicPlatform.ready(function() { // เตรียมก่อนเรียกใช้ plugin return $cordovaToast .show(str, duration, position) .then(function(success) { // success }, function (error) { // error }); }); }; var db = null; $ionicPlatform.ready(function() { // เตรียมก่อนเรียกใช้ plugin // ตรวจสอบและทำการเชื่อมต่อกับ db db = window.sqlitePlugin.openDatabase({ name: 'my.db', location: 'default' }, function (db) { // เชื่อมต่อ db สำเร็จ $scope.showToast('Open DB Success','long','bottom'); // เริ่มทำงานของคำสั่ง db db.transaction(function (tx) { // เวลามีการทดสอบและเพิ่มฟิลด์หรือแก้ไขตารางควรเปิดคอมเม้นการลบตารางก่อน // นั้นหมายถึงตารางจะถูกสร้างและจัดรูปแบบใหม่ทุกครั้ง ข้อมูลตารางจะรีเซ็ต ล้างค่า // พอว่างโครงสร้างตารางเรียบร้อยแล้ว ให้ปิดคอมเม้นไว้เหมือนเดิม เพื่อให้ข้อมูลยังคงอยู่ // tx.executeSql('DROP TABLE IF EXISTS setting'); // ทำคำสั่งสร้างตาราง setting ถ้ายังไม่มี ในที่นี้เราเก็บแค่ setting_id และ member_id tx.executeSql('' + 'CREATE TABLE IF NOT EXISTS setting ' + '(setting_id integer primary key,' + 'member_id integer,notify_status interger)' + ''); // เช็คตาราง setting ว่ามีข้อมูลไหม tx.executeSql(''+'SELECT setting_id,member_id,' + 'notify_status FROM setting', [], function(tx, res) { if(res.rows.length<=0){ // ถ้าไม่มีข้อมูล ให้เพิ่มข้อมูลไป var query = "INSERT INTO setting" + " (member_id,notify_status) " + " VALUES (?,1)"; var member_id = 0; // ทำงานคำสั่ง sql tx.executeSql(query, [member_id], function(tx, res) { // เมื่อทำการบันทึกข้อมูลสำเร็จ // ค่าเริ่มต้น เราจะให้ member_id เป็น 0 ไว้ก่อน แล้วค่อยมาอัพเเดทที่หลัง $scope.showToast('insertId: ' + res.insertId + ' -- probably 1','long','bottom'); $scope.showToast('rowsAffected: ' + res.rowsAffected + ' -- should be 1','long','bottom'); }, function(tx, error) { $scope.showToast('INSERT error: ' + error.message,'long','bottom'); }); }else{ // ถ้ามีข้อมูลอยู่แล้ว และ member_id ที่เก็บไว้ไม่เท่ากับ 0 $scope.setNotify(res.rows.item(0).notify_status); if(res.rows.item(0).member_id!=0){ // เช็ค member_id ที่เก็ยไว้ไม่เท่ากับ 0 $scope.setMemberID(res.rows.item(0).member_id); // กำหนดค่าตัวแปร $state.go('app.profile'); // ไปที่หน้าโพรไฟล์ } } }); }, function (error) { $scope.showToast('transaction error: ' + error.message,'long','bottom'); }, function () { $scope.showToast('transaction ok','long','bottom'); }); }, function (error) { $scope.showToast('Open database ERROR: ' + JSON.stringify(error),'long','bottom'); }); }); // สร้างฟังก์ชั่นสำหรับ logout $scope.logoutMember = function(){ $ionicPlatform.ready(function() { // เตรียมก่อนเรียกใช้ plugin // ขึ้นแจ้งเตือนว่าต้องการจะออกจากระบบหรือไม่ $cordovaDialogs.confirm('ยืนยันออกจากระบบ?', 'ออกจากระบบ?', ['ตกลง','ยกเลิก']) .then(function(buttonIndex) { // no button = 0, 'OK' = 1, 'Cancel' = 2 var btnIndex = buttonIndex; if(btnIndex==2){ // ถ้าตอบ ยกเลิก return false; // หยุดการทำงาน ไม่ต้องออกจากระบบ } if(btnIndex==1){ // ถ้าตอบ ตกลง ยืนบันออกจากรับบ // เริ่มทำงานของคำสั่ง db db.transaction(function (tx) { // ล้างค่า member_id ที่บันทึกไว้ในตาราง setting ให้เป็น 0 คือไม่จำ member_id var query = "UPDATE setting SET member_id = 0 WHERE setting_id = 1"; tx.executeSql(query); $scope.sesMemberID = null; $state.go('app.login'); // ไปที่หน้าล็อกอิน }, function(error) { $scope.showToast('transaction error: ' + error.message,'long','bottom'); }, function() { $scope.showToast('transaction ok','long','bottom'); }); } }); }); }; // กำหนดตัวแปรไว้สำหรับเก็บ notify status ในเครื่องนั้นๆ $scope.sesNotify = null; // สร้างฟังก์ชั่นสำหรับกำหนดค่า notify status ให้สามารถเรียกใช้งานจาก $scope หลักได้ $scope.setNotify = function(status){ $scope.sesNotify = status; }; // สร้างฟังก์ชั่นสำหรับเรียกดูค่า notify status ของสมาชิกจาก $scope หลักได้ $scope.getNotify = function(){ return $scope.sesNotify; }; })
สังเกตให้ส่วนของคำสั่งสร้างตาราง
tx.executeSql('' + 'CREATE TABLE IF NOT EXISTS setting ' + '(setting_id integer primary key,' + 'member_id integer,notify_status interger)' + '');
เราได้เพิ่มฟิลด์ notify_status เก็บเป็น interger ไว้เก็บค่า 0 กับ 1
เมื่อสร้างตารางแล้ว เราจะให้ทำคำสั่ง ดึงข้อมูลในตาราง ในขั้นตอนนี้เราก็ต้อง
ดึงฟิลด์ notify_status ด้วย ตามคำสั่ง
tx.executeSql(''+'SELECT setting_id,member_id,' + 'notify_status FROM setting', [], function(tx, res) {
ปล. สังเกตคำสั่ง sql จะมีเป็นลักษณะการใช้การ + string ที่ขึ้นบรรทัดใหม่ ถ้าเขียนติดกันยาวๆ
จะได้เป็น
tx.executeSql('SELECT setting_id,member_id,notify_status FROM setting', [], function(tx, res) {
คำสั่งข้างต้น จะดึงค่ากรณีมีข้อมูล ไปใช้ในการแสดงหน้า setting ซึ่งถ้าเป็นการติดตั้ง app ครั้งแรก
ก็จะไมีมีข้อมูล ก็จะไปทำงานคำสั่งเพิ่มข้อมูล
var query = "INSERT INTO setting" + " (member_id,notify_status) " + " VALUES (?,1)"; var member_id = 0; // ทำงานคำสั่ง sql tx.executeSql(query, [member_id], function(tx, res) {
จะเห็นว่าในส่วนของการบันทึกข้อมูล เราจะส่งค่าแค่ค่าเดียวตัวตัวกำหนด ( ? )
tx.executeSql(query, [member_id], function(tx, res) {
คำสังนี้เราส่งแค่ member_id ส่วนค่า notify_status เรากำหนดค่าตายตัว ไปในคำสั่งแล้วเป็น 1
ข้างต้นเป็นกรณีไม่มีข้อมูล ส่วนกรณีมีข้อมูลในตารางแล้ว เราก็จะเข้าเงื่อนไข นำข้อมูล
ไปกำหนดค่า ผ่านฟังก์ชั่น
}else{ // ถ้ามีข้อมูลอยู่แล้ว และ member_id ที่เก็บไว้ไม่เท่ากับ 0 $scope.setNotify(res.rows.item(0).notify_status); if(res.rows.item(0).member_id!=0){ // เช็ค member_id ที่เก็ยไว้ไม่เท่ากับ 0 $scope.setMemberID(res.rows.item(0).member_id); // กำหนดค่าตัวแปร $state.go('app.profile'); // ไปที่หน้าโพรไฟล์ } }
เงื่อนไขคือมีข้อมูลการตั้งค่า คำอธิบายด้านบนอธิบายผิด member_id เป็นค่าว่างก็ได้
บรรทัดต่อมาเราจะเห็นการใช้ฟังก์ชั่น เพื่อส่งค่าสถานะ notify_status ไปกำหนดในตัวแปร
// นำค่าจาก database ส่งค่าไปในฟังก์ชั่น กำหนดค่า sesNotify $scope.setNotify(res.rows.item(0).notify_status);
ค่าตัวแปร ฟังก์ชั่นกำหนดค่า และฟังก์ชั่นเรียกใช้ค่า เรากำหนดในส่วนของ AppCtrl ตามนี้
// กำหนดตัวแปรไว้สำหรับเก็บ notify status ในเครื่องนั้นๆ $scope.sesNotify = null; // สร้างฟังก์ชั่นสำหรับกำหนดค่า notify status ให้สามารถเรียกใช้งานจาก $scope หลักได้ $scope.setNotify = function(status){ $scope.sesNotify = status; }; // สร้างฟังก์ชั่นสำหรับเรียกดูค่า notify status ของสมาชิกจาก $scope หลักได้ $scope.getNotify = function(){ return $scope.sesNotify; };
3. สร้างหน้า setting สำหรับให้ผู้ใช้เปลี่ยนค่าสะานะการรับข้อความ push
ดูไฟล์ settings.html ในโฟลเดอร์ templates
<ion-view view-title="Settings"> <ion-nav-buttons side="right"> <div class="buttons buttons-right header-item"> <span class="right-buttons"> <button class="button button-icon button-clear ion-android-more-vertical"> </button> </span> </div> </ion-nav-buttons> <ion-content ng-class="{expanded:isExpanded}"> <div class="list"> <ion-toggle class="border-item" ng-model="notify_status_checked" ng-checked="notify_status_checked" ng-change="changeNotiy()" toggle-class="toggle-calm-900"> Push Notification </ion-toggle> </div> <div class="padding"> {{notify_status_checked}} </div> </ion-content> </ion-view>
สังเกตว่าเราใช้ directive ion-toggle ของ ionic (directive ก็คล้ายกับรูปแบบแท็กเฉพาะที่สร้างขึ้นมา แล้ว
จะถูกแปลงในรูปแบบแท็กพื้นฐาน html อีกที) ลักษณะ toggle ก็จะคล้าย checkbox หรือคล้ายกับ สวิชเปิดปิด
ng-model="notify_status_checked" คือ ค่าของ toggle ให้เท่ากับตัวแปร notify_status_checked ng-checked="notify_status_checked" คือ ค่าการเปิดปิด ให้เป็นไปตามค่าของตัวแปร notify_status_checked ng-change="changeNotiy()" คือ ถ้ามีการเปลี่ยนแปลงค่า ให้ไปทำงานฟังก์ชั่น changeNotify() toggle-class="toggle-calm-900" คือ ส่วนของการกำหนด css class
โดยค่าตัวแปร และฟังก์ชั่น เราจะเขียนไว้ในส่วนของการจัดการหรือ controller
จากโค้ดเรามีการแทรก คำสั่งการแสดงค่าตัวแปร เพื่อให้เห็นภาพ
<div class="padding"> {{notify_status_checked}} </div>
4. จัดการการทำงานหน้า settng ใน SettingCtrl
ในไฟล์ controllers.js ในโฟลเดอร์ js ให้เราดูในส่วนของ SettingCtrl
หรือส่วนควบคุมการทำงาหน้า setting
.controller('SettingCtrl', function( $scope, $stateParams, $timeout, ionicMaterialInk, ionicMaterialMotion ,$ionicPlatform, $cordovaToast, $cordovaDevice, notifyService) { $scope.$parent.showHeader(); $scope.$parent.clearFabs(); $scope.$parent.setHeaderFab(false); $scope.$parent.hasShadow(); $timeout(function() { $scope.isExpanded = false; $scope.$parent.setExpanded(false); }, 300); // สร้างฟังก์ชั่นสำหรับเรียกใช้ Toast plugin $scope.showToast = function(str, duration, position){ $ionicPlatform.ready(function() { // เตรียมก่อนเรียกใช้ plugin return $cordovaToast .show(str, duration, position) .then(function(success) { // success }, function (error) { // error }); }); }; var db = null; $ionicPlatform.ready(function() { // เตรียมก่อนเรียกใช้ plugin // ตรวจสอบและทำการเชื่อมต่อกับ db db = window.sqlitePlugin.openDatabase({ name: 'my.db', location: 'default' }, function (db) { $scope.showToast('Open DB Success','long','bottom'); }, function (error) { $scope.showToast('Open database ERROR: ' + JSON.stringify(error),'long','bottom'); }); }); $scope.notify_status = $scope.$parent.getNotify(); $scope.notify_status_checked = ($scope.notify_status==1)?true:false; $scope.changeNotiy = function(){ $ionicPlatform.ready(function() { // เตรียมก่อนเรียกใช้ plugin // alert($scope.notify_status_checked); var statusNotify = null; var statusNotify_val = 0; if ($scope.notify_status_checked == true) { $scope.notify_status_checked = false; // alert(0); statusNotify_val = 0; statusNotify = 'false'; } else { $scope.notify_status_checked = true; // alert(1); statusNotify_val = 1; statusNotify = 'true'; } // เริ่มทำงานของคำสั่ง db db.transaction(function (tx) { // คำอธิบายในโค้ดตัวอย่างในไฟล์ที่แจก บรรทัดนี้ผิด ดูคำอธิบายด้านล่าง var query = "UPDATE setting SET notify_status = ? WHERE setting_id = 1"; tx.executeSql(query, [statusNotify_val], function(tx, res) { // เมื่อทำการบันทึกข้อมูลสำเร็จ // เตรียมข้อมูลสำหรับส่งไปบันทึกบน server $scope.$parent.setNotify(statusNotify_val); var dataSend = { uid: $cordovaDevice.getUUID(), status: statusNotify }; // service เรียกใช้ฟังก์ชั่น notifyService ส่งค่าไปยัง server notifyService.updateNotify(dataSend) .then(function (response) { // ทดสอบแสดงค่าว่าบันทึกสำเร็จหรือไม่ // alert(JSON.stringify(response)); }); }, function(tx, error) { $scope.showToast('Update error: ' + error.message,'long','bottom'); }); }, function (error) { $scope.showToast('transaction error: ' + error.message, 'long', 'bottom'); }, function () { $scope.showToast('transaction ok', 'long', 'bottom'); }); }); }; ionicMaterialMotion.fadeSlideInRight(); ionicMaterialInk.displayEffect(); })
จะเห็นว่าเรามีการ inject หรือเรียกใช้ $cordovaDevice และ notifyService
โดย $cordovaDevice เราจะใช้ดึงค่า UUID รหัสเฉพาะของเครื่องมือถือนั้น ส่งไปเช็ค
ค่าในตาราง tbl_notify บน server (เนื้อหาอ้างอิงบทความที่ผ่านมา เนื่องจากเรามีการ
เก็บค่า UUID และ registrandid สำหรับมือถือที่รับข้อความ push แต่เริ่มต้นแล้ว)
ส่วน nofityService เราจะเพิ่มฟังก์ชั่นการอัพเดทข้อมูลเข้าไป จากเดิม มีเพียงคำสั่งเพิ่มข้อมูล
ทำความเข้าใจในส่วน SettingCtrl ขออธิบายเฉพาะเนื้อหาใหม่
ส่วนแรก เราจะไปดึงค่าสถานะ จาก controller หลัก ผ่านฟังก์ชัน getNotify()
มาไว้ในตัวแปร notify_status ซึ่งกำหนดในรูปแบบ $scope.notify_status
สังเกตว่าการเรียกฟังชั่นจาก controller หลัก เราจะใช้ $scope.$parent. เพื่ออ้างอิง controller หลัก
แล้วตามด้วยฟังก์ชั่น ที่ต้องการ
$scope.notify_status = $scope.$parent.getNotify(); $scope.notify_status_checked = ($scope.notify_status==1)?true:false;
ส่วนต่อมาคือตัวแปรสถานะการเปิดปิด toggle จะใช้เป็น true หรือ false
โดยถ้าค่า $scope.notify_status==1 ให้ notify_status_checked เป็น true ถ้าเป็นอื่นให้มีค่าเป็น false
นั่นคือ ถ้าใน database มีการบันทึกเป้น 1 แล้ว ตัว toggle ในหน้า setting ก็จะแสดงเป็นสะานะเปิด ตาม
ค่าของตัวแปร notify_status_checked
ต่อไปมาดูส่วนของฟังก์ชั่น ขออธิบายในโค้ด
// เมื่อมีการเปลี่ยนค่า toggle $scope.changeNotiy = function(){ $ionicPlatform.ready(function() { // เตรียมก่อนเรียกใช้ plugin // ค่าที่ส่งเข้ามาของตัวแปร notify_status_checked จะเป็นค่าของสถานะก่อนเปลี่ยนค่า // alert($scope.notify_status_checked); var statusNotify = null; // สร้างตัวแปรเก็บสถานะแบบข้อความ var statusNotify_val = 0; // สร้างตัวแปรเก็บถานะแบบตัวเลข if ($scope.notify_status_checked == true) { // ค่าเดิมเป็น true เมื่อเปลี่ยน $scope.notify_status_checked = false; // ให้กำหนดค่าหใม่เป็น false // alert(0); statusNotify_val = 0; statusNotify = 'false'; } else { //เมื่อค่าเดิมเป็น false $scope.notify_status_checked = true; // กำหนดค่าใหม่เป็น true // alert(1); statusNotify_val = 1; statusNotify = 'true'; } // เริ่มทำงานของคำสั่ง db db.transaction(function (tx) { // ทำคำสั่ง อัพเดทสถานะ notify_status ค่าที่อัพเดทเราจะใช้เป็นตัวแปรที่เก็บตัวเลข 0 หรือ 1 var query = "UPDATE setting SET notify_status = ? WHERE setting_id = 1"; tx.executeSql(query, [statusNotify_val], function(tx, res) { // เมื่อทำการอัพเดทสำเร็จ $scope.$parent.setNotify(statusNotify_val); // อัพเดทค่าในตัวแปรใน controller หลักด้วย // เตรียมข้อมูลสำหรับส่งไปบันทึกบน server สัเกตสถาน เราจะส่งเป็นข้อความ ไปแทนโดยใช้ // เป็นข้อความ 'true' หรือ 'false' var dataSend = { uid: $cordovaDevice.getUUID(), status: statusNotify }; // service เรียกใช้ฟังก์ชั่น updateNotify จาก notifyService ส่งค่าไปยัง server notifyService.updateNotify(dataSend) .then(function (response) { // ทดสอบแสดงค่าว่าบันทึกสำเร็จหรือไม่ // alert(JSON.stringify(response)); }); }, function(tx, error) { $scope.showToast('Update error: ' + error.message,'long','bottom'); }); }, function (error) { $scope.showToast('transaction error: ' + error.message, 'long', 'bottom'); }, function () { $scope.showToast('transaction ok', 'long', 'bottom'); }); }); };
5. ส่วนของ service ที่เพิ่มฟังก์ชั่น อัพเดทข้อมูล
จากในข้อที่ 4 เรามีการใช้งาน notifyService ให้ทำงานต่อจากบันทึกข้อมูลลง database บนเครื่องแล้ว
ให้ทำงานอัพเดทสถานะบน server ด้วย ดูไฟล์ services.js ในโฟลเดอร์ js
.service('notifyService',function($http, $q){ // สร้าง service // กำหนด url ของ ไฟล์ api ของเรา var url = "http://192.168.8.100/app/api/notify_api.php"; return { // ในที่นี้เราจะใช้การส่งค่าแบบ post setNotify: function(dataSend) { var deferred = $q.defer(); $http.post(url,dataSend) .success(deferred.resolve) .error(deferred.reject); return deferred.promise; }, updateNotify: function(dataSend) { var deferred = $q.defer(); $http.post(url,dataSend) .success(deferred.resolve) .error(deferred.reject); return deferred.promise; } }; })
มีส่วนของฟังก์ชั่น updateNotify() เพิ่มเข้ามา ส่งค่าไปทำงานที่ไฟล์ notify_api.php ที่ฝั่ง server
โดยโค้ด notify_api.php เราก็เพิ่มในส่วนของเงื่อนไขการอัพเดทเข้าไปด้วย จะได้เป็น
ไฟล์ notify_api.php
<?php header("Content-type:application/json; charset=UTF-8"); header("Cache-Control: no-store, no-cache, must-revalidate"); header("Cache-Control: post-check=0, pre-check=0", false); if(isset($_SERVER['HTTP_ORIGIN'])) { header("Access-Control-Allow-Origin: {$_SERVER['HTTP_ORIGIN']}"); header('Access-Control-Allow-Credentials: true'); // header('Access-Control-Max-Age: 86400'); // cache for 1 day } // Access-Control headers are received during OPTIONS requests if($_SERVER['REQUEST_METHOD'] == 'OPTIONS') { if(isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD'])) header("Access-Control-Allow-Methods: GET, POST, OPTIONS"); if(isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS'])) header("Access-Control-Allow-Headers: {$_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']}"); exit(0); } // ส่วนการกำหนด header ให้กำหนดตามด้านบน ไม่เช่นนั้น app จะไม่สามารถส่งค่ามาได้ require_once("dbconnect.php"); $postdata = file_get_contents("php://input"); // ค่าที่ส่งมาจะถูกเก็บที่ไฟล์ if(isset($postdata)) { // ถ้ามีคาที่ส่งมา $_POST = json_decode($postdata,true); // แปลงค่าที่ส่งมาเป็นตัวแปร array $_POST } $_error_msg = null; $_success_msg = null; if( isset($_POST['regid']) && $_POST['regid']!="" && isset($_POST['uid']) && $_POST['uid']!="" ){ // ส่วนของเงื่อนไขเพิ่มข้อมูล $_res_regID = $mysqli->real_escape_string(trim($_POST['regid'])); $_res_UID = $mysqli->real_escape_string(trim($_POST['uid'])); if(isset($_error_msg) && $_error_msg!=""){ $json_data[]=array( "success" => $_success_msg, "error" => $_error_msg ); }else{ // เช็คในตารางก่อนว่า เครื่องมือถือนั้นๆ ได้เคยบันทึกข้อมูลมาก่อนแล้วหรือไม่ อิงจาก UUID $sql=" SELECT notify_regid,notify_uid,notify_status FROM tbl_notify WHERE notify_uid = '".$_res_UID."' "; $result = $mysqli->query($sql); if($result && $result->num_rows>0){ // ถ้าเคยบันทึกแล้ว $row = $result->fetch_assoc(); // ดึงค่ามาตรวจสอบ $_success_msg=1; if($row['notify_regid'] != $_res_regID){ // ถ้าค่าเดิม กับค่าหใม่ไม่เหมือนกัน ให้อัพเดทค่าใหม่ $sql2 = " UPDATE tbl_notify SET notify_regid = '".$_res_regID."' , notify_status = '1' , notify_updateadd = '".date('Y-m-d H:i:s')."' WHERE notify_uid = '".$_res_UID."' "; $result2 = $mysqli->query($sql2); if($result2 && $mysqli->affected_rows>0){ // ถ้าอัพเดทสำเร็จ $_success_msg=1; $json_data[]=array( "success" => $_success_msg, "error" => $_error_msg, "regid" => $_res_regID, "uid"=> $_res_UID, "status"=> 1 ); }else{ // ถ้าอัพเดทไม่สำเร็จ $json_data[]=array( "success" => $_success_msg, "error" => $_error_msg ); } }else{ // ถ้าค่าใหม่ เท่ากับค่าเดิม ใช้ค่าเดิม $json_data[]=array( "success" => $_success_msg, "error" => $_error_msg, "regid" => $row['notify_regid'], "uid" => $row['notify_uid'], "status" => $row['notify_status'] ); } }else{ // ถ้ายังไม่มีข้อมูล ให้บันทีกข้อมูลใหม่ $sql2 = " INSERT INTO tbl_notify SET notify_uid = '".$_res_UID."' , notify_regid = '".$_res_regID."' , notify_status = '1' , notify_dateadd = '".date('Y-m-d H:i:s')."' "; $result2 = $mysqli->query($sql2); if($result2 && $mysqli->affected_rows>0){ // ถ้าบันทึกขอ้มูลสำเร็จ $_success_msg=1; $json_data[]=array( "success" => $_success_msg, "error" => $_error_msg, "regid" => $_res_regID, "uid"=> $_res_UID, "status"=> 1 ); }else{ // ถ้าบันทึกข้อมูลล้มเหลว $json_data[]=array( "success" => $_success_msg, "error" => $_error_msg ); } } } // }elseif( isset($_POST['status']) && $_POST['status']!="" && isset($_POST['uid']) && $_POST['uid']!="" ){ // ส่วนของเงื่อนไขอัพเดทข้อมูล $_res_STATUS = $mysqli->real_escape_string(trim($_POST['status'])); $_res_UID = $mysqli->real_escape_string(trim($_POST['uid'])); $_res_STATUS = ($_res_STATUS=='true')?1:0; if(isset($_error_msg) && $_error_msg!=""){ $json_data[]=array( "success" => $_success_msg, "error" => $_error_msg ); }else{ $sql=" SELECT notify_id,notify_regid FROM tbl_notify WHERE notify_uid = '".$_res_UID."' "; $result = $mysqli->query($sql); if($result && $result->num_rows>0){ $row = $result->fetch_assoc(); $_success_msg=1; $sql2 = " UPDATE tbl_notify SET notify_status = '".$_res_STATUS."' , notify_updateadd = '".date('Y-m-d H:i:s')."' WHERE notify_uid = '".$_res_UID."' "; $result2 = $mysqli->query($sql2); if($result2 && $mysqli->affected_rows>0){ $_success_msg=1; $json_data[]=array( "success" => $_success_msg, "error" => $_error_msg, "regid" => $row['notify_regid'], "uid"=> $_res_UID, "status"=> $_res_STATUS ); }else{ $json_data[]=array( "success" => $_success_msg, "error" => $_error_msg ); } }else{ $sql2 = " INSERT INTO tbl_notify SET notify_uid = '".$_res_UID."' , notify_status = '".$_res_STATUS."' , notify_dateadd = '".date('Y-m-d H:i:s')."' "; $result2 = $mysqli->query($sql2); if($result2 && $mysqli->affected_rows>0){ $_success_msg=1; $json_data[]=array( "success" => $_success_msg, "error" => $_error_msg, "regid" => '', "uid"=> $_res_UID, "status"=> $_res_STATUS ); }else{ $json_data[]=array( "success" => $_success_msg, "error" => $_error_msg ); } } } }else{ // ถ้าไม่ได้กรอกข้อมลใดๆ เขามา $_error_msg = "โปรดกรอกข้อมูลให้ครบถ้วน"; $json_data[]=array( "success" => $_success_msg, "error" => $_error_msg ); } // แปลง array เป็นรูปแบบ json string if(isset($json_data)){ $json= json_encode($json_data); if(isset($_GET['callback']) && $_GET['callback']!=""){ echo $_GET['callback']."(".$json.");"; }else{ echo $json; } } ?>
โดยในไฟล์ php เมื่อมีสถานะเป็นข้อความ เป็นคำว่า 'true' ส่งค่ามาอัพเดท ก็เปลี่ยนค่าเป้น 1
ถ้าเป็นอื่นเปลี่ยนค่าเป็น 0 แล้วบันทึกสถานะการรับข้อความ push ของผู้ใช้นั้นๆ
และในส่วนของการส่งข้อความ push เราก็เพิ่มเงื่อนไขตอนดึงข้อมูล ให้ดึงเฉพาะผู้ใช้ที่เปิดรับข้อความ
push ตัวอย่าง
ไฟล์ test_push.php ที่มีเงื่อนไข
<?php header("Content-type:text/html; charset=UTF-8"); header("Cache-Control: no-store, no-cache, must-revalidate"); header("Cache-Control: post-check=0, pre-check=0", false); // เชื่อมต่อกับฐานข้อมูล require_once("dbconnect.php"); // ส่วนตั้งค่าการใช้งาน push require_once("pusher.php"); $fcmToken = "your_fcm_token"; // ค่าา FCM token $pusher = new Pusher($fcmToken); $regId=array(); $sql=" SELECT * FROM tbl_notify WHERE notify_status = 1 "; $result = $mysqli->query($sql); if($result){ while($row = $result->fetch_assoc()){ $regId[]=$row['notify_regid']; } } $msg = array ( 'message' => 'ทดสอบข้อความภาษาไทย', 'title' => 'ทดสอบหัวข้อหลัก', 'subtitle' => 'ทดสอบหัวข้อย่อย', 'tickerText' => 'Ticker text here...Ticker text here...Ticker text here', 'vibrate' => 1, 'sound' => 1, 'count' => 2, 'image' => 'https://www.ninenik.com/images/banner_ninenik.gif', 'largeIcon' => 'large_icon', 'smallIcon' => 'small_icon' ); $pusher->notify($regId, $msg); echo "<pre>"; print_r($pusher->getOutputAsArray()); echo "</pre>"; ?>
ทั้งหมดเป็นการอธิบายรูปแบบการทำงาน การกำหนดการตั้งค่าในหน้า setting สำหรับกำหนดสถานะ
push notification ให้ศึกษาเป็นแนวทางต่อไป