สำหรับเนื้อหานี้จะเป็นตอนที่สอง ต่อจากตอนที่แล้ว ที่เราวางโครงสร้างรูปแบบ
คร่าวๆ ของ app ว่าจะเป็นในลักษณะไหน
แนวทางใช้งาน ionic material สร้างระบบสมาชิก ร่วมกับ SQLite
https://www.ninenik.com/content.php?arti_id=734 via @ninenik
โดยในตอนที่สอง จะเป็นการประยุกต์ให้สามารถใช้งานได้จริง ตั้งแต่การสมัคร
สมาชิก แล้วบันทึกลงฐานข้อมูล จากนั้นลิ้งค์ไปหน้าโพรไฟล์ ดึงข้อมูลจากฐาน
ข้อมูลมาแสดงในหน้าโพรไฟล์ และเมื่อล็อกเอาท์ออกจากระบบ ก็จะแสดงหน้า
ล็อกอิน ให้ผู้ใช้ทำการกรอก username และ password ใหม่อีกครั้งถ้าต้องการ
ล็อกอิน ประมาณนี้เป็นต้น
ก่อนอื่น เราต้องตรวจสอบ cordova plugin ที่จำเป็นเพิ่มเติมก่อน ซึ่งจะได้แก่
SQLite - สำหรับใช้งาน db cordova-sqlite-storage 1.4.8 "Cordova sqlite storage plugin" Toast - สำหรับแสดงสถานะการทำงาน cordova-plugin-x-toast 2.5.2 "Toast" Dialogs - สำหรับขึ้นข้อความแจ้งเตือน หรือแจ้งการทำงานcordova-plugin-dialogs cordova-plugin-dialogs 1.2.1 "Notification" SpinnerDialog - สำหรับขึ้นแสดง loading พร้อมข้อความ (เคยติดตั้งแล้วในบทความ map) cordova-plugin-spinner-dialog 1.3.1 "SpinnerDialog"
1. ตรวจสอบ plugin ด้วยคำสั่ง
C:\phonegap\learn001>phonegap plugin ls
หากยังไม่ได้ติดตั้ง plugin ข้างต้น ให้ทำการติดตั้งด้วยคำสั่ง ดังนี้
ติดตั้ง plugin SQLite ด้วยคำสั่ง
C:\phonegap\learn001>cordova plugin add cordova-sqlite-storage
ติดตั้ง plugin Toast ด้วยคำสั่ง
C:\phonegap\learn001>cordova plugin add cordova-plugin-x-toast
ติดตั้ง plugin Dialogs ด้วยคำสั่ง
C:\phonegap\learn001>cordova plugin add cordova-plugin-dialogs
ติดตั้ง plugin SpinnerDialog ด้วยคำสั่ง
C:\phonegap\learn001>cordova plugin add cordova-plugin-spinner-dialog
2. กำหนดในส่วนของ controller ของหน้าล็อกอิน หน้าสมัครสมาชิก และหน้าโพรไฟล์
ในส่วนของ controller ให้เปิดไฟล์ controllers.js ในโฟลเดอร์ js ขึ้นมา
แล้วปรับโค้ดให้เป็นดังนี้ (คำอธิบายแสดงในโค้ด หากมีอธิบายเพิ่มเติมจะแสดงด้านล่างโค้ด)
AppCtrl ส่วนของ controller หลัก
.controller('AppCtrl', function($scope, $ionicModal, $ionicPopover, $timeout, $ionicPopup) {
$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;
};
})
สังเกตว่าเรามีการเพิ่ม ตัวแปร sesMemberID เข้ามาเพื่อใช้เก็บค่า member_id ของสมาชิก
รวมทั้งมีการสร้างฟังก์ชั่นกำหนดค่า และฟังก์ชั่นเรียกใช้งานค่า member_id เพื่อไว้สำหรับเรียกใช้
จาก controller ย่อยอื่นๆ ได้
LoginCtrl ส่วนของ controller หน้า login
การทำงานของหน้านี้คือ เริ่มกำหนดค่าว่างในตัวแปรที่ใช้ในฟอร์มล็อกอิน ตรวจสอบข้อมูลที่กรอก
ในฟอร์มล็อกอินว่ากรอกข้อมูลครบหรือไม่ จากนั้นส่งค่าข้อมูลไปตรวจสอบกับ db ว่ามีข้อมูล
ของสมาชิกนั้นหรือไม่ หากมีข้อมูลก็ให้เข้าสู่ระบบ และไปยังหน้าโพรไฟล์
.controller('LoginCtrl', function($scope, $timeout, $stateParams, ionicMaterialInk
,$ionicPlatform, $cordovaDialogs, $cordovaToast, $state) {
$scope.$parent.clearFabs();
$scope.$parent.hasShadow();
$timeout(function() {
$scope.$parent.hideHeader();
}, 0);
// กำนหดค่าเริ่มต้นของฟอร์มหน้าสมัครสมาชิก ให้เป็นค่าว่าง
$scope.login = {
input_user:'',
input_pass:''
};
// เรียกใช้ฟังก์ชั่นจาก AppCtrl หลัก เพื่อซ่อนเมนูสมาชิก โดยส่งค่า false เข้าไป
$scope.$parent.setMemberMenu(false);
// สร้างฟังก์ชั่นสำหรับเรียกใช้ 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');
});
});
// สร้างฟังก์ชั่นสำหรับล็อกอิน รับค่า data จาก object login
$scope.loginMember = function(data){
$ionicPlatform.ready(function() { // เตรียมก่อนเรียกใช้ plugin
// ตรวจสอบถ้ากรอกข้อมูลไม่ครบ
if(data.input_user=="" || data.input_pass==""){
$cordovaDialogs.alert('โปรดกรอกข้อมูลให้ครบถ้วน', 'ข้อมูลจำเป็น', 'ตกลง')
.then(function() {
return false;
});
return false;
}
// แสดงข้อมูลที่ส่งเข้ามา เวลาใช้จริงสามารถลบออกได้
$scope.showToast(' user: ' + data.input_user +
' pass: ' + data.input_pass +'', 'long', 'bottom');
// เริ่มทำงานของคำสั่ง db
db.transaction(function (tx) {
// กำหนดคำสั่ง sql ค่าที่รับมาตรวจสอบ แทนด้วย ?
// คำสั่งนี้คือเช็คว่า มีสมาชิกที่ user และ pass ตรงหรือไม่ ตัวเล็กตัวใหญ่มีค่าต่างกัน
var query = "SELECT member_id, member_user, member_phone" +
" FROM member WHERE member_user = ? AND member_pass = ? ";
// ทำคำสั่ง sql ส่งค่าเข้าไปให้ตรงกับจำนวน ในที่นี้มี data.input_user และ data.input_pass
tx.executeSql(query, [data.input_user, data.input_pass], function (tx, resultSet) {
// ถ้าพบรายการใน db
if(resultSet.rows.length){
// นำค่า ID ของสมาชิกไปเก็บในตัวแปร sesMemberID ด้วยฟังก์ชั่น setMemberID
// ที่อยู่ใน controller หลัก
$scope.$parent.setMemberID(resultSet.rows.item(0).member_id);
// ขึ้นแจ้ง เข้าสู่ระบบสำเร็จ และเปลี่ยนไปหน้า profile ด้วย $stat.go()
$cordovaDialogs.alert('เข้าสู่ระบบสำเร็จ', 'เข้าสู่ระบบ', 'ตกลง')
.then(function() {
$state.go('app.profile');
});
}else{
// ไม่พบข้อมูล ขึ้นแจ้งเตือนว่ามีข้อผิดพลาด
$cordovaDialogs.alert('ชื่อหรือรหัสผ่านไม่ถูกต้อง', 'เกิดข้อผิดพลาด', 'ตกลง')
.then(function() {
return false;
});
return false;
}
},
function (tx, error) {
// ข้อความแจ้ง SELECT error
$scope.showToast('SELECT error: ' + error.message,'long','bottom');
});
}, function(error) {
$scope.showToast('transaction error: ' + error.message,'long','bottom');
}, function() {
$scope.showToast('transaction ok','long','bottom');
});
});
};
ionicMaterialInk.displayEffect();
})
สังเกตว่าใน LoginCtrl มีการเรียกใช้งาน $ionicPlatform, $cordovaDialogs, $cordovaToast, $state เพิ่มเข้ามา โดย
$ionicPlatform สำหรับตรวจสอบความพร้อมของอุปกรณ์ก่อนเรียกใช้ plugin
$cordovaDialogs สำหรับ alert แจ้งเตือนข้อความ
$cordovaToast สำหรับข้อความแจ้งสถานะ
$state สำหรับเชื่อมโยงหรือลิ้งค์ไปหน้าต่างๆ
RegistCtrl ส่วนของ controller หน้า register
การทำงานหน้านี้คือ เริ่มกำหนดตัวแปรค่าว่างให้กับข้อมูลในฟอร์มสมัครสมาชิก
ทำการตรวจสอบการกรอกข้อมูลในฟอร์มก่อนส่งข้อมูล ทำการส่งข้อมูลไปบันทึกใน db
จากนั้นลิ้งค์ไปยังหน้าโพรไฟล์
.controller('RegistCtrl', function($scope, $timeout, $stateParams, ionicMaterialInk
,$ionicPlatform, $cordovaDialogs, $cordovaToast, $state) {
$scope.$parent.clearFabs();
$scope.$parent.hasShadow();
$timeout(function() {
$scope.$parent.hideHeader();
}, 0);
// กำนหดค่าเริ่มต้นของฟอร์มหน้าสมัครสมาชิก ให้เป็นค่าว่าง
$scope.reg = {
input_user:'',
input_pass:'',
input_phone:''
};
// เรียกใช้ฟังก์ชั่นจาก AppCtrl หลัก เพื่อซ่อนเมนูสมาชิก โดยส่งค่า false เข้าไป
$scope.$parent.setMemberMenu(false);
// สร้างฟังก์ชั่นสำหรับเรียกใช้ 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 member');
// ทำคำสั่งสร้างตาราง member ถ้ายังไม่มี
tx.executeSql('' +
'CREATE TABLE IF NOT EXISTS member ' +
'(member_id integer primary key,' +
'member_user text, ' +
'member_pass text,' +
'member_phone text)' +
'');
}, 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');
});
});
// สร้างฟังก์ชั่นสำหรับสมัครสมาชิก รับค่า data จาก object reg
$scope.registerMember = function(data){
$ionicPlatform.ready(function() { // เตรียมก่อนเรียกใช้ plugin
// ตรวจสอบให้กรอกข้อมูลให้ครบถ้วน
if(data.input_user=="" || data.input_pass=="" || data.input_phone==""){
$cordovaDialogs.alert('โปรดกรอกข้อมูลให้ครบถ้วน', 'ข้อมูลจำเป็น', 'ตกลง')
.then(function() {
return false;
});
return false;
}
// แสดงข้อมูลที่ส่งเข้ามา เวลาใช้จริงสามารถลบออกได้
$scope.showToast(' user: ' + data.input_user +
' pass: ' + data.input_pass +
' phone: ' + data.input_phone +'', 'long', 'bottom');
// เริ่มทำงานของคำสั่ง db
db.transaction(function (tx) {
// จัดรูปแบบคำสั่ง sql สำหรับบันทึกข้อมูล ค่าที่รับมาบันทึกจะแทนด้วย ?
// ในที่นี้จะส่งมาแค่ 3 ค่า ส่วน member_id นั้นเป็น PRIMARY KEY เป็น auto incremet
// โดยอัตโนมัติ
var query = "INSERT INTO member" +
" (member_user, member_pass, member_phone) " +
" VALUES (?,?,?)";
// ทำงานคำสั่ง sql
tx.executeSql(query, [data.input_user, data.input_pass,
data.input_phone], function(tx, res) {
// เมื่อทำการบันทึกข้อมูลสำเร็จ
// ใช้ค่า member_id จาก res.insertId แล้วกำหนดค่าด้วยฟังก์ชั่น setMemberID()
$scope.$parent.setMemberID(res.insertId);
$scope.showToast('insertId: ' + res.insertId + ' -- probably 1','long','bottom');
$scope.showToast('rowsAffected: ' + res.rowsAffected + ' -- should be 1','long','bottom');
// แจ้งการสมัครสมาชิกสำเร็จ และให้ไปที่หน้า profile
$cordovaDialogs.alert('ทำการสัมครสมัครสมาชิกเรียบร้อยแล้ว', 'สมัครสมาชิกใหม่', 'ตกลง')
.then(function() {
$state.go('app.profile');
});
},
function(tx, error) {
$scope.showToast('INSERT error: ' + error.message,'long','bottom');
});
}, function(error) {
$scope.showToast('transaction error: ' + error.message,'long','bottom');
}, function() {
$scope.showToast('transaction ok','long','bottom');
});
});
};
ionicMaterialInk.displayEffect();
})
ProfileCtrl ส่วนของ controller หน้า profile
การทำงานของหน้านี้ทำการเชื่อมกับ db แล้วไปดึงข้อมูลของสมาชิกที่ทำการล็อกอิน
สำหรับ หรือสมาชิกที่สมัครสมาชิกมาแสดง โดยอิงการดึงค่าจาก member_id ที่มีการเก็บ
ค่าไว้ใน $scope ที่อยู่ใน controller หลัก เมื่อพบข้อมูลก็นำมากำหนดในตัวแปร แล้วเรียกใช้
งานในหน้าโพรไฟล์อีกที
.controller('ProfileCtrl', function(
$scope, $stateParams, $timeout,
ionicMaterialMotion, ionicMaterialInk, $ionicPlatform,
$cordovaSpinnerDialog, $cordovaToast) {
$scope.$parent.showHeader();
$scope.$parent.clearFabs();
$scope.isExpanded = false;
$scope.$parent.setExpanded(false);
$scope.$parent.setHeaderFab(false);
$scope.$parent.hasShadow();
// กำหนดตัวแปรสำหรับค่าเริ่มต้นของข้อมูลหน้าโพรไลฟ์
$scope.member_user = '';
$scope.member_phone = '';
// กำหนดให้แสดงเมนูสมาชิก
$scope.$parent.setMemberMenu(true);
// สร้างฟังก์ชั่นสำหรับเรียกใช้ 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');
// แสดง spin dialog plugin จะใช้หรือไม่ก็ได้ นำมาใช้เผื่อใครไปประยุกต์เพิ่ม
$cordovaSpinnerDialog.show(null,"รอสักครู่..กำลังโหลดข้อมูล"); // แสดง loading
$timeout(function() { // ซ่อนอัตโนมัติใน 300 มิลลิวินาที
$cordovaSpinnerDialog.hide();
}, 300);
// เริ่มทำงานของคำสั่ง db
db.transaction(function (tx) {
// เตรียมคำสั่ง sql ดึงข้อมูลสมาชิกอิงจาก member_id ที่เราเก็บไว้ใน $scope หลัก
var query = "SELECT member_user, member_phone" +
" FROM member WHERE member_id = ?";
// ดึงค่า member_id จาก $scope หลักด้วยฟังก์ชั่น getMemberID()
var id_member = $scope.$parent.getMemberID();
// ทำคำสั่ง sql ดึงข้อมูลตาม member_id
tx.executeSql(query, [id_member], function (tx, resultSet) {
// ถ้าพบข้อมูล
if(resultSet.rows.length){
// นำข้อมูลที่ได้ มาไว้ในตัวแปร member_user และ member_phone
// เพื่อแสดงในหน้า profile
$scope.member_user = resultSet.rows.item(0).member_user;
$scope.member_phone = resultSet.rows.item(0).member_phone;
}
$cordovaSpinnerDialog.hide();
},
function (tx, error) {
$scope.showToast('SELECT error: ' + error.message,'long','bottom');
});
}, 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');
});
});
$timeout(function() {
ionicMaterialMotion.slideUp({
selector: '.slide-up'
});
}, 300);
$timeout(function() {
ionicMaterialMotion.fadeSlideInRight({
startVelocity: 3000
});
}, 700);
ionicMaterialInk.displayEffect();
})
3. ทำการ build apk ไฟล์แล้วนำไปทดสอบติดตั้งบนมือถือ
ด้วยคำสั่ง
C:\phonegap\learn001>phonegap build android
ให้ทำการทดสอบติดตั้งในมือถือ android ของเรา จะได้หน้าตา app ประมาณนี้
ในตัวอย่างมีส่วนของแผนที่ด้วย หากต้องการเห็นผลแผนที่ด้วย
อาจจะต้อง รีสตาร์ทเครื่องหนึ่งครั้ง

