# 13. 通訊錄 手作 API 2
# 載入 cdn
- Vue
- Axios
# 建立 Vue 實體
使用立即函式封裝,指派 el
並建立 data
。
// 我的寫法
(function(){
let vm = new Vue({
el: '#app',
data: {
contacts: [],
input: {
name: '',
email: ''
}
}
})
})()
// Alex 的寫法
;(function(Vue){
new Vue({
el: '#app',
data: {
contacts: [],
input: {
name: '',
email: ''
}
}
})
})(Vue)
# 取得資料 (GET)
# 在 mounted 階段取得資料
以我的寫法為例:
- 在
mounted
階段,讓網頁 DOM 掛載完成後開始執行動作。 - 顯示 loading 畫面
- 使用變數
loading
和v-if
、v-else
控制畫面。 - AJAX 尚未載入完成時,
loading = true
顯示 loading 畫面。
- 使用變數
- 運作 JSON Server
- 執行
npm run json
來運作 JSON Server,得到資料庫路徑。 - 如果沒有執行
npm run json
,瀏覽器會一直在 loading 畫面。
- 執行
- 取得 JSON 資料
- 使用
axios.get("資料庫位置")
取得 JSON 資料。 - 在成功取得資料後,關掉 loading 畫面 (
loading = false
)。
- 使用
HTML
<div id="app">
<!-- 尚未載入完成時顯示的畫面 -->
<section v-if="loading">
Loading...
</section>
<!-- 載入完成後顯示的畫面 -->
<section v-else>
// 主要程式碼
</section>
</div>
JS
// 我的寫法
(function(){
let vm = new Vue({
el: '#app',
data: {
contacts: [],
input: {
name: '',
email: ''
},
loading: false // 控制 loading 畫面切換
},
// DOM 掛載完之後取得資料並關閉 loading 畫面
mounted(){
this.loading = true
axios
.get('http://localhost:8888/contact')
.then((res) => {
this.contacts = res.data
this.loading = false
})
.catch((err) => {
console.log(err)
})
}
})
})()
# 將取得的資料放到網頁上
把透過 AJAX 取得的資料取代假資料顯示到網頁上。
- 雙向綁定輸入框與資料
input
用v-model
綁定輸入欄位。- 使用修飾詞
.trim
去除頭尾空白。
- 陳列聯絡人資訊
contacts
用v-for
跟列出資料庫中的聯絡人資訊。
<section v-else>
<div class="container">
<div class="title">
<div class="input">
<input type="text" placeholder="會員名稱" v-model.trim="input.name" />
<input type="text" placeholder="電子信箱" v-model.trim="input.email" />
<button>送出</button>
<button>取消</button>
</div>
<div class="menu">
<div class="menuItem" v-for="(item,index) in contacts">
<span class="number">{{ index + 1 }}</span>
<span class="type">{{ item.name }}</span>
<a :href="'mailto:'+item.email" class="title">{{ item.email }}</a>
<span class="edit">修改</span>
<span class="delete">刪除</span>
</div>
</div>
</div>
</div>
</section>
# 新增資料 (POST)
# 綁定點擊事件
在輸入資料的區塊,將「送出」和「取消」按鈕各自綁定點擊事件。
<div class="input">
<input
type="text"
placeholder="會員名稱"
v-model.trim="input.name"
/>
<input
type="text"
placeholder="電子信箱"
v-model.trim="input.email"
/>
<button @click="submitHandler">送出</button>
<button @click="cancelHandler">取消</button>
</div>
# 撰寫事件邏輯進行處理
然後到 Vue 裡面用 methods
來進行處理。
- 取消:把
input
內容清空。 - 送出:將
input
的內容加到資料庫。- 如果
name
或email
任意欄位空白,則跳出不執行。 - 使用 loading 頁面。
- 使用
axios.post(資料庫位置, 要新增的資料)
將資料新增到資料庫。- 把這筆資料加到
contacts
的陣列中。 - 執行
cancelHandler
來清空input
。 - 若有使用 loading 頁面,記得要切換回來。
- 把這筆資料加到
- 如果
methods: {
// 送出
submitHandler(){
let {name, email} = this.input
if(!name || !email) return
this.loading = true
axios
.post('http://localhost:8888/contact', this.input)
.then((res) => {
this.contacts.push(res.data)
this.cancelHandler()
this.loading = false
})
.catch((err) => {
console.log(err)
})
},
// 取消
cancelHandler(){
// 寫法一
this.input.name = '',
this.input.email = ''
// 寫法二
this.input = {
name: '',
email: ''
}
}
}
# 刪除資料 (DELETE)
# 綁定事件
在「刪除」按鈕綁上用來刪除的點擊事件,並且傳入那一項在陣列中的索引值,以判斷要刪除的是哪一筆資料。
<span class="delete" @click="deleteHandler(index)">刪除</span>
# 撰寫事件邏輯刪除資料
然後到 Vue 裡面用 methods
來進行處理。
- 使用 loading 頁面。
- 使用
confirm()
讓使用者再次確認,以免誤刪。 - 使用
axios.delete(該筆資料位置)
將該筆資料從資料庫中刪除。- 畫面上也要用
splice()
將該筆資料從contacts
陣列中刪除。 - 執行
cancelHandler
來清空input
。以防按了「修改」後又按了「刪除」,結果被刪的資料還留在input
裡面。 - 若有使用 loading 頁面,記得要切換。
- 畫面上也要用
methods: {
deleteHandler(index){
this.loading = true
if(confirm(`是否刪除 ${this.contacts[index].name}`)){
axios
.delete('http://localhost:8888/contact/' + this.contacts[index].id])
.then((res) => {
this.contacts.splice(index, 1)
this.cancelHandler()
this.loading = false
})
.catch((err) => {
console.log(err)
})
} else {
this.loading = false
}
}
}
# 修改資料 (PUT)
修改資料時,流程如下:
- 點擊某筆資料的「修改」按鈕。
- 將該筆資料的內容放到對應的
input
欄位。 - 按下「送出」按鈕修改資料庫和畫面。
# 點擊某筆資料的「修改」按鈕
在「修改」按鈕綁上用來修改的點擊事件,並且傳入那一項在陣列中的索引值,以判斷要修改的是哪一筆資料。
<span class="edit" @click="editHandler(index)">修改</span>
# 將該筆資料的內容放到對應的 input
欄位。
然後到 Vue 裡面用 methods
來進行處理。
- 判斷被點擊「修改」按鈕的是
contacts
陣列中的哪一筆資料,然後將資料放到對應的input
位置。 - 由於新增跟修改同樣要使用「送出」按鈕,為了辨別是要新增還是修改資料,建立變數
editIndex
並預設為null
。- 當資料被點擊「修改」按鈕後,
editIndex
的值為該資料的id
。 - 若修改過程中反悔想取消,按下「取消」按鈕時,也要讓
editIndex
回歸到null
。 - 若修改過程中刪除了資料,一樣要清空
input
,並且editIndex
要回到null
。
- 當資料被點擊「修改」按鈕後,
data: {
editIndex: null
},
methods: {
editHandler(index){
let {name, email} = this.contacts[index]
this.input = {name, email}
this.editIndex = index
},
cancelHandler(){
this.editIndex = null
this.input.name = '',
this.input.email = ''
}
}
# 按下「送出」按鈕修改資料庫和畫面
由於修改和新增共用同一個按鈕,因此當「送出」按鈕被點擊時,原本的 submitHandler
函式要先進行判斷,再執行不同的工作。
- 當
editIndex
為null
時,表示按下「送出」是要新增資料。 - 當
editIndex
為陣列中的索引值 (index
) 時,則修改該筆資料。- 使用
axios.put(該筆資料位置, 要修改的資料)
修改資料庫中指定資料的內容。- 畫面上該筆資料原本的內容,會被修改後的內容取代。
- 執行
cancelHandler
來清空input
。 - 若有使用 loading 頁面,記得要切換。
- 使用
methods: {
submitHandler(){
let {name, email} = this.input
if(!name || !email) return
this.loading = true
// 使用 editIndex 來區別要新增或修改
if(this.editIndex == null) {
axios
.post('http://localhost:8888/contact', this.input)
.then((res) => {
this.contacts.push(res.data)
this.cancelHandler()
this.loading = false
})
.catch((err) => {
console.log(err)
})
} else {
axios
.put('http://localhost:8888/contact/' + this.contacts[this.editIndex].id, this.input)
.then((res) => {
this.contacts[this.editIndex] = res.data
this.cancelHandler()
this.loading = false
})
.catch((err) => {
console.log(err)
})
}
}
}
# 小結
- GET 資料
mounted
階段- 使用 GET 方法,取得資料後存到陣列,並切換 loading 。
mounted() {
console.log('Vue in mounted...');
this.loading = true;
axios
.get('http://localhost:8888/contact')
.then((res) => {
this.contacts = res.data;
this.loading = false;
})
.catch((err) => {
console.log(err);
});
},
- POST 資料
- 取消:清空輸入框
- 新增:
- 使用 POST 推送資料到伺服器。
- 將資料用
.push()
加到陣列、清空輸入框、切換 loading 狀態。
- DELETE 資料
- 先判斷要刪除哪一筆,再使用 DELETE 刪除指定資料。
- 將資料用
.splice()
刪除、清空輸入框、切換 loading 狀態。
- PUT 資料
- 先判斷要修改哪一筆,將該筆資料內容放到對應的輸入框。
- 由於「新增」與「修改」共用同一個「送出」按鈕,因此需先判斷行為。
- 建立變數
editIndex
幫助判斷行為。- 如果要修改,則值為該筆資料的
index
,執行 PUT。 - 若為非修改、取消修改、修改中刪除資料,則值為
null
,執行 POST。
- 如果要修改,則值為該筆資料的
data: {
contacts: [],
input: {
name: '',
email: '',
},
loading: false,
editIndex: null,
},
methods: {
submitHandler() {
let { name, email } = this.input;
if (!name || !email) return;
this.loading = true;
if (this.editIndex == null) {
axios
.post('http://localhost:8888/contact', { name, email })
.then((res) => {
this.contacts.push(res.data);
this.cancelHandler();
this.loading = false;
})
.catch((err) => {
console.log(err);
});
} else {
axios
.put(
'http://localhost:8888/contact/' +
this.contacts[this.editIndex].id,
this.input
)
.then((res) => {
this.contacts[this.editIndex] = res.data;
this.cancelHandler();
this.loading = false;
})
.catch((err) => {
console.log(err);
});
}
},
cancelHandler() {
this.editIndex = null;
this.input.name = '';
this.input.email = '';
},
editHandler(index) {
let { name, email } = this.contacts[index];
this.input = { name, email };
this.editIndex = index;
},
deleteHandler(index) {
this.loading = true;
if (confirm(`是否刪除 ${this.contacts[index].name} ?`)) {
axios
.delete(
'http://localhost:8888/contact/' + this.contacts[index].id
)
.then((res) => {
this.contacts.splice(index, 1);
this.cancelHandler();
this.loading = false;
})
.catch((err) => {
console.log(err);
});
} else {
this.loading = false;
}
},
}
});