2022-05-27|閱讀時間 ‧ 約 29 分鐘

CRUD x Vue.js x PHP x MYSQL PDO

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 - 刪除
vue.js 生命週期
vue.js 生命週期
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>&times;</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編撰的規格

&times; => 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">&times;</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();
		}
	});
給有需要的朋友~
謝謝~宅見~
分享至
成為作者繼續創作的動力吧!
© 2024 vocus All rights reserved.