S1.建立資料庫 user
-
-- 資料表結構 `users`
--
CREATE TABLE `users` (
`name` varchar(20) NOT NULL,
`email` text NOT NULL,
`password` text NOT NULL,
`id` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
S2.建立 index.htm
a.載入 vue.js、jquery、bootstrap
b.版面建立
S3.vue.js程式
新增、查看列表、互動視窗、修改、刪除
S4.PHP程式 x CRUD
增刪查改(CRUD) Create - 建立 Read -讀取、查詢 Update - 更新 Delete - 刪除
index.htm
<!--
20201031 by JokerWu
20220527 新修-步驟拆解 & 加註版
_1=建立資料
_2=建立列表
_3=互動視窗
_4=完整
-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Vue.js PHP MYSQL</title>
</head>
<!--
程式中 變數、函式命名有
駝峰式命名法
每一個單字的首字母都採用大寫字母
例如:FirstName、LastName、CamelCase
也被稱為Pascal命名法
-->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="http://code.jquery.com/jquery-3.6.0.min.js"></script>
<!--
jquery 可以改成 axios套件來處理AJAX
Axios是很輕量的套件,只有約13kb
jQuery比較笨重 主要是用來處理AJAX
所以axios 在vue.js 來說是較好的選擇
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
-->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js"></script>
<style>
table, th, td {
border: 1px solid black;
border-collapse: collapse;
}
th, td {
padding: 25px;
}
</style>
<body>
<!-- 版面 -->
<div id="myApp"> <!-- 此處的 id 跟 vue.js //el: 互動 -->
<div class="container"><!-- .container 是BS5的預設類別, 是一個RWD 固定寬度的內容容器 -->
<h1 class="text-center">建立資料</h1>
<div class="row">
<div class="col-md-6">
<form method="POST" action="create.php" v-on:submit.prevent="doCreate">
<!--
v-on:submit.prevent
submit ->表單送出
.prevent->不換頁刷新頁面
doCreate->vue.js 自訂義函式 用來將表單資料 以JSON格式 送出至create.php
v-on:submit.prevent可簡寫為
@:submit.prevent
-->
<div class="form-group">
<label>姓名</label>
<input type="text" name="name" class="form-control" />
</div>
<div class="form-group">
<label>Email</label>
<input type="email" name="email" class="form-control" />
</div>
<div class="form-group">
<label>密碼</label>
<input type="password" name="password" class="form-control" />
</div>
<input type="submit" value="建立" class="btn btn-primary" />
</form>
</div>
</div>
<!-- 版面 列表頁 -->
<h1 class="text-center">列表</h1>
<table class="table">
<tr>
<th>排序ID</th>
<th>姓名</th>
<th>Email</th>
<th>管理</th>
</tr>
<!--
以下為使用 vue.js for迴圈
參考範例 v04.html & v05.html
只要把 v-for 放在一個 <li> 上就有 for 的效果
item 是參數(你也可為它取別的名字)用來表示陣列元素
list 指的是自定義的陣列名稱(陣列元素為數個物件)
in 也可以寫成 of,因為這樣更接近原生 JavaScript 疊代器的語法
v-for 還支援第二個參數 — — 索引值(index)
帶入索引值參數的方法是在原本 item 的位置改寫成 (item, index) 即可
此處 v-for 放在一個<tr>內使用
陣列元素 以 user 命名 ->代表 資料表內的欄位名稱
索引值 以 index 命名 ->表示第幾筆資料
而 users 為 從 vue.js 收到JSON資料 解出後 的資料集名稱
-->
<tr v-for="(user, index) in users">
<td v-text="user.id"></td>
<td v-text="user.name"></td>
<td v-text="user.email"></td>
<td>
<button type="button" v-bind:data-id="user.id" v-on:click="showEditUserModal" class="btn btn-primary">編輯</button>
<!--
v-bind
v03.html範例
雙花括號 填資料的方法,不適用 HTML 屬性 因此應該把 v-bind: 加在 HTML 屬性前面,且在屬性對應的值中放置參數,在 Model 中定義要傳入的值。v-bind 的縮寫是去掉 v-bind、只留下一個冒號 :
v-bind 是將狀態綁定到 HTML 元素上
因此
v-bind:data-id="user.id"
此處可改為
:data-id="user.id"
v-bind:data-id 此處的data-id 是為了帶值到彈出視窗(互動視窗 或叫 Modal)內的 user.id 欄位
.getAttribute(''):取得屬性->DOM語法
選取元素時用元素的屬性去選 e.target.getAttribute('data-value')
btn-primary ->BS5 預設按鈕樣式
v-on:click 可參照 v06.html & v06_01.html
v-on: 事件 = "方法名稱()"
v-on: 的縮寫是 @
-->
<form method="POST" action="delete.php" v-on:submit.prevent="doDelete" style="display: contents;">
<!--
display: contents
此處使用暴力法 直接用 Style 處理
display->改變元素對外所參與的佈局環境 或 對內佈局環境,提供後面元素佈局的規則
也就是 這個form層 不套用CSS格式 但子層套用
詳細範例可參照 1.htm
-->
<input type="hidden" name="id" v-bind:value="user.id" />
<input type="submit" name="submit" class="btn btn-danger" value="刪除" />
</form>
</td>
</tr>
<!-- vue.js for迴圈 End -->
</table>
</div>
<!-- 列表 END -->
<!-- Modal 互動視窗-->
<div class="modal fade" id="editUserModal">
<div class="modal-dialog" role="document">
<!--
Bootstrap 一次只支援一個互動視窗。
role屬性的目的是識別解析軟件元素及其子元素
modal用法
modal.show() //顯示 modal
modal.hide() //隱藏 modal
modal.destroy() //移除 Modal 產生的 dom 元素與 event listeners
以下為 BS5 modal 結構
「data-backdrop="static"」 鎖定背景,點擊背景時不自動關閉視窗
「fade」 淡入、淡出的轉場效果
「modal-lg」視窗大小,如modal-lg、modal-md、modal-sm
「data-dismiss="modal"」 關閉視窗
「data-keyboard="true"」 是否用ESC鍵關閉,預設為true
-------
<div id="OOXX" class="modal fade" tabindex="-1" role="dialog" aria-hidden="true" data-backdrop="static" data-keyboard="true">
<div class="modal-dialog modal-lg" >
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">
<span>×</span>
-------
aria-label = alt (讀螢幕的軟體可以用 無障礙空間 就像 html 的 role屬性
<a href="#" title="設置"><img src="gear.png"></a> => title="設置" 不會唸出
<a href="#" aria-label="設置"><img src="gear.png"></a> => aria-label="設置" 可唸出
詳細 可參照 WAI-ARIA W3C編撰的規格
× => X 符號
-->
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">修改資料</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<form method="POST" action="update.php" v-on:submit.prevent="doUpdate" id="form-edit-user" v-if="user != null">
<!--
v-if 請參照 v10.htm
-->
<input type="hidden" name="id" v-bind:value="user.id" />
<div class="form-group">
<label>姓名</label>
<input type="text" name="name" v-bind:value="user.name" class="form-control" />
</div>
<!--
class="form-control" =>BS5 form表單控制
可以使用 .form-control-lg 和 .form-control-sm 設置高度
不然就不要填 用預設
-->
<div class="form-group">
<label>Email</label>
<input type="email" name="email" v-bind:value="user.email" class="form-control" />
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">關閉</button>
<button type="submit" name="submit" class="btn btn-primary" form="form-edit-user">確定修改</button>
</div>
</div>
</div>
<!-- Modal 互動視窗 END -->
<!-- 下方是 頭myApp 跟 內容container 的 div-->
</div>
</div>
<!--
直譯式程式
即時互動 如 BS.js 、 jQuery、Vue.js
一般都放後面
-->
<script src="VueControl.js"></script>
VueControl.js
const myApp = new Vue({
el: "#myApp", //el: HTML '元素 id'
data: { //資料結構 版面要用的那些資料 從這來的
users: [],//JSON讀取來的資料 或 view版面過來的 初始宣告 為空的 users
user: null //初始宣告變數 user 主要是給互動視窗用
},
methods: {
//建立資料函式
doCreate: function () {
const self = this; //this 關鍵字使用 此處使用 this可方便使用且資料比較不會錯亂
const form = event.target; //事件DOM元件 此處使用 可控制表單那些欄位
const ajax = new XMLHttpRequest(); //AJAX 通訊初始
ajax.open("POST", form.getAttribute("action"), true);
//使用AJAX 將 form 表單 的元素 action 建立
//意思就是 以AJAX即時不刷新 POST的通訊方式
//將form表單資料傳輸至 action="create.php"
ajax.onreadystatechange = function () {
/*
ajax.onreadystatechange
當資料有變化時
觸發事件AJAX
XMLHttpRequest.readyState
客戶端物件目前的狀態
0 UNSENT 客戶端已被建立,但 open() 方法尚未被呼叫。
1 OPENED open() 方法已被呼叫。
2 HEADERS_RECEIVED send() 方法已被呼叫,而且可取得 header 與狀態。
3 LOADING 回應資料下載中,此時 responseText 會擁有部分資料。
4 DONE 完成下載操作。
xmlhttp.status的值及解釋:可查表 較常遇到
200——成功
202——接受和處理、但處理未完成
400——錯誤請求,如語法錯誤
401——請求授權失敗
404——沒有發現檔案、查詢或URl
*/
if (this.readyState == 4) {
if (this.status == 200) {
// console.log(this.responseText);
const user = JSON.parse(this.responseText);
//JSON.parse() 方法把會把一個JSON字串轉換成JavaScript的數值或是物件。
//資料處理
self.users.unshift(user);
/*
陣列資料 原型:Array.prototype.unshift()
將資料加到第一個
例如:
array.unshift('老媽');
console.log(array); // ["老媽", "小明", "杰倫", "漂亮阿姨", "小美"]
與 push() 雷同
*/
}
}
};
//建立form表單資料傳輸 使用 AJAX 即時刷新
const formData = new FormData(form);
ajax.send(formData);
},
//-------建立資料函式 END
//取得所有資料 建立列表頁
getData: function () {
const self = this;
const ajax = new XMLHttpRequest();
ajax.open("POST", "read.php", true);
ajax.onreadystatechange = function () {
if (this.readyState == 4) {
if (this.status == 200) {
// console.log(this.responseText);
const users = JSON.parse(this.responseText);
self.users = users; //將取得的資料 帶進users 給vue.js data用
}
}
};
const formData = new FormData();
ajax.send(formData);
},
//-------
//建立 互動視窗函式
showEditUserModal: function () {
const id = event.target.getAttribute("data-id");
//使用AJAX 將 列表頁的 v-bind:data-id="user.id"
//傳送接收過來
//比對 users 陣列內 有沒有 傳送過來的 user.id 值
//有的話 給互動視窗用的變數 user 改成這一個
for (var a = 0; a < this.users.length; a++) {
if (this.users[a].id == id) {
this.user = this.users[a];
break;
}
}
$("#editUserModal").modal("show"); //將互動視窗顯示
/*
modal用法
modal.show() //顯示 modal
modal.hide() //隱藏 modal
modal.destroy() //移除 Modal 產生的 dom 元素與 event listeners
*/
},
//-------建立 互動視窗函式 END
//更新函式
doUpdate: function () {
const self = this;
const form = event.target;
const ajax = new XMLHttpRequest();
ajax.open("POST", form.getAttribute("action"), true);
//此處AJAX 取得來自互動視窗的 form表單
//action="update.php" 從這一頁 產生的 JSON 資料
//---資料改變 START --
ajax.onreadystatechange = function () {
if (this.readyState == 4) {
if (this.status == 200) {
// console.log(this.responseText);
const user = JSON.parse(this.responseText);
// console.log(user);
//接下來要進行 原本資料排序的資料處理
//因為資料更改後 原users陣列資料會與更新的不同
//因此先找出來該筆資料 原理與line bot 範例同
var index = -1; //為避免麻煩 直接使用本程式不可能出現的數字 如7秒魚範例手法
for (var a = 0; a < self.users.length; a++) {
if (self.users[a].id == user.id) {
index = a;
break;
}
}
// 建立暫存陣列 準備進行陣列重整
//宣告的暫存資料 等於 JSON來的 更新過陣列資料
const tempUsers = self.users;
//將取得有變化的陣列索引 改變成 互動視窗用的 user 變數
tempUsers[index] = user;
// update the local array by removing all old elements and inserting the updated users
//清掉 users 成為空陣列
//在將 users 變成 暫存的陣列 也就是新的資料
self.users = [];
self.users = tempUsers;
}
}
};
//---資料改變 END --
//表單資料 用AJAX 不跳轉頁面傳送
const formData = new FormData(form);
ajax.send(formData);
//隱藏互動視窗
$("#editUserModal").modal("hide");
},
//更新函式 END
//刪除函式
doDelete: function () {
const self = this;
const form = event.target;
const ajax = new XMLHttpRequest();
ajax.open("POST", form.getAttribute("action"), true);
ajax.onreadystatechange = function () {
if (this.readyState == 4) {
if (this.status == 200) {
// console.log(this.responseText);
//移除 users 陣列的資料
for (var a = 0; a < self.users.length; a++) {
if (self.users[a].id == form.id.value) {
self.users.splice(a, 1);
/*
使用arr.splice(要插入或刪除的索引位置, 要刪除的元素數量, 要插入的元素內容)
此處我們使用 a = 位置 索引值
然後再用第二個參數 刪除的元素數量=1
一次只刪一個
*/
break;
}
}
}
}
};
const formData = new FormData(form);
ajax.send(formData);
},
//刪除函式 END
//-----
},//方法結束
//使用AJAX 進行監聽讀取資料 詳細請參照 vue.js 生命週期(lifecycle)圖 mounted
mounted: function () {
this.getData();
}
});
給有需要的朋友~
謝謝~宅見~