---
# System prepended metadata

title: '2019/12/28 VueJS 教學筆記: 用 JSON-SERVER 來模擬 AXIOS RESTFUL API 修改資料'
tags: [JSON-Server, AXIOS, VueJS]

---

---
title: '2019/12/28 VueJS 教學筆記: 用 JSON-SERVER 來模擬 AXIOS RESTFUL API 修改資料'
disqus: hackmd
---

2019/12/28 VueJS 教學筆記: 用 JSON-SERVER 來模擬 AXIOS RESTFUL API 修改資料
===

綱要

[TOC]

複習第一天上過的AXIOS API環境建立
---

如果你已經忘記了怎麼使用AXIOS的GET、POST、DELETE行為可以回到 [2019/09/07 VueJS 教學筆記: 生命週期與AXIOS API](https://hackmd.io/@FortesHuang/SycpsQ08B) 看看基本的AXIOS封裝方法。

這次上課我們要增加一個PUT的行為來做編輯指定資料的模擬功能。

增加PUT的行為
---

AXIOS內我們先來設定一個PUT行為封裝：

**api.js**

```javascript=
import axios from 'axios'

const api = {
  headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
  // get
  // ...
  // post
  // ...
  put: (url, params) => {
    return new Promise((resolve, reject) => {
      axios.put(url, params)
        .then((response) => {
          resolve(response.data)
        })
        .catch((error) => {
          reject(error)
        })
    })
  },
  
  // delete
  // ...
}ㄏ

export default api
```

接著回到以前做過的`ApiTemp.vue`
然後用另一個cmd視窗啟動JSON-Server: `json-server --watch db.json`

**ApiTemp.vue**

![](https://i.imgur.com/V2jb3tI.png)


```htmlmixed=
<template>
  <div class="row column">
    <!--新增一個新的表單-->
    <el-form label-width="8rem" data-width="20rem" data-space="space-vertical">
      <el-form-item label="ID">
        <el-input v-model="postObj.id" type="text" />
      </el-form-item>
      <el-form-item label="Name">
        <el-input v-model="postObj.name" type="text" />
      </el-form-item>
      <el-form-item label="Class">
        <el-input v-model="postObj.class" type="text" />
      </el-form-item>
      <el-form-item label="Date">
        <el-date-picker
          v-model="postObj.date"
          type="datetime"
          value-format="yyyy-MM-dd HH:mm:ss"
          :placeholder="$t('select_datetime')"
        />
      </el-form-item>
      <div class="row horizontal v_center end">
        <el-button @click="packagePostData">Add Data(POST)</el-button>
      </div>
    </el-form>
    <!-- 沿用先前的el-table -->
    <el-table :data="tableData" style="width: 100%; overflow: hidden;" height="50vh">
      <el-table-column prop="id" label="ID" align="center" width="100" />
      <el-table-column prop="name" label="Name" align="center" width="100" />
      <el-table-column prop="class" label="Class" />
      <el-table-column prop="date" label="Date" />
      <el-table-column label="Action" align="center">
        <template slot-scope="scope">
          <el-button type="danger" @click="delCheck(scope.row.id)">DELETE</el-button>
          <!--增加一個EDIT用的Button，綁定個openEditDialog的事件，並給予這行table資料中的物件作為參數-->
          <el-button type="warning" @click="openEditDialog(scope.row)">EDIT</el-button>
        </template>
      </el-table-column>
    </el-table>
    <!--編輯內容使用的Dialog-->
    <el-dialog title="編輯" :visible.sync="editDialog">
      <el-form label-width="8rem" data-width="20rem" data-space="space-vertical">
        <el-form-item label="ID">
          <el-input v-model="editForm.id" type="text" />
        </el-form-item>
        <el-form-item label="Name">
          <el-input v-model="editForm.name" type="text" />
        </el-form-item>
        <el-form-item label="Class">
          <el-input v-model="editForm.class" type="text" />
        </el-form-item>
        <el-form-item label="Date">
          <el-date-picker
            v-model="editForm.date"
            type="datetime"
            value-format="yyyy-MM-dd HH:mm:ss"
            :placeholder="$t('select_datetime')"
          />
        </el-form-item>
      </el-form>
      <span slot="footer" class="dialog-footer">
        <el-button @click="editDialog = false">取消</el-button>
        <el-button @click="updateData" type="primary">送出</el-button>
      </span>
      </el-dialog>
    </el-dialog>
  </div>
</template>
```
```javascript=
import { getLanguage, setLanguage } from "../../lang"
import { stringify } from 'querystring'
export default {
  name: "apiTemp",
  data() {
    return {
      lang: 'zh_tw',
      tableData: [],
      postObj: {
        id: '',
        name: '',
        class: '',
        date: ''
      },
      editForm: {},
      editDialog: false,
    }
  },
  
  created() {
    getLanguage
    this.packageGetData()
  },
  
  methods: {
    async packageGetData() {
      // 需要修改一下source url，使GET Data後，先在JSON Server中自動排序一次
      const url = 'http://localhost:3000/tableData?_sort=id'
      let res = await this.$api.get(url)
      console.log(res)
      this.tableData = [...res]
      // 正式環境才有後端給來的result回應，這裡只做註解
      // if (res.result == 1)  {
      //   console.log(res)
      //   this.tableData = [...res.data]
      // } else {
      //   console.log('API error')
      // }
    },
    
    async packagePostData() {
      const source = 'http://localhost:3000/tableData'
      const currentDate = new Date().toJSON().slice(0, 10)
      await this.$api.post(source, this.postObj)
      this.packageGetData()
      console.log(source.data)
    },
    
    // 防止刪除按鈕誤觸，加上確認刪除提醒和成功與失敗時的提示
    
    async delCheck(id) {
      try {
        await this.$confirm(
          '將會刪除資料，是否繼續？', '刪除資料', {
          confirmButtonText: '確認刪除',
          cancelButtonText: '取消',
          type: 'warning'
        })
        await this.delData(id)
        this.$message({
          message: '刪除成功',
          type: 'success'
        })
      }
      catch {
        this.$message({
          message: '已取消删除',
          type: 'info'
        })
      }
    },
    
    async delData(id) {
      const source = 'http://localhost:3000/tableData/' + `${id}`
      await this.$api.delete(source)
      this.packageGetData()
      console.log(this.tableData)
    },
    
    /* 加入編輯資料的事件 */
    
    /* 
     一開始必須先建置一個打開對話彈出視窗Dialog的事件，之後再由Dialog中的確認Button
    去觸發PUT做Update資料
    */
    
    openEditDialog(obj) {
      this.editDialog = true // 改變editDialog的狀態打開Dialog
      // 直接將el-table該處scope.row的參數物件繼承給editForm，這樣打開視窗時裡面就會有對應資料內容顯示出來
      this.editForm = {...obj} 
      console.log('edit obj: ', this.editForm)
    },
    
    async updateData() {
      let id = this.editForm.id
      // 指定修改對象的id
      const source = 'http://localhost:3000/tableData/' + `${id}`
      /* 
       * async/await中可以使用 try/catch 來讓內置的Promise()出現問題時
       * 可以在catch中指定的事件反應出來，反之在try當中就是成功操作資料修改 
      */
      try {
        await this.$api.put(source, this.editForm)
        this.$message({
          message: '編輯成功',
          type: 'success'
        })
        this.editDialog = false
        console.log('ID ' + id + ' 編輯成功')
      }
      catch {
        this.$message.error('編輯失敗')
      }
      this.packageGetData()
    }
  }
}
```

現在我們可以試試看編輯其中一筆資料看看結果會如何：

![](https://i.imgur.com/9pbonoT.png)

PUT與PATCH的不同
---

PUT真正意義上的動作應該是 Replace (Create or Update)，也就是替換特定位置(ex: tableData/1)的資料，假如位置中沒有這筆資料，就Create新增進去。

但如果我們只需要更改一個屬性，卻得將整筆資料貼入，看起來實在有點佔用頻寬。
因此後來就有了PATCH這個方法讓我們可以只針對單一屬性做變更。

比如原本該筆資料POST新增後的物件內容是這樣：

```javascript=
{
  "id": "001",
  "name": "Claire",
  "class": "Survivor",
  "date": "2019-12-26 00:00:00"
}
```

以PATCH的方式更新資料某個屬性就可以這樣做：

**api.js**

```javascript=
const api = {
  // ...
  // 封裝一個Patch的Promise()
  patch: (url, params) => {
    return new Promise((resolve, reject) => {
      axios.patch(url, params)
        .then((response) => {
          resolve(response.data)
        })
        .catch((error) => {
          reject(error)
        })
    })
  },
}
```
**ApiTemp.vue**

```javascript=

async patchData() {
  let id = this.editForm.id
  const source = 'http://localhost:3000/tableData/' + `${id}`
  
  try {
    await this.$api.patch(source, { name: 'Hunk' }) // 直接替換name後送出
  }

  catch {
    this.$message.error('編輯失敗')
  }
  
  this.packageGetData()
}
```

可是話說回來，UI設計原本就是整張表單皆需要填寫資料才做Submit，中間又有表單驗證需要處理，所以通常正式環境只會使用POST來做所有的資料操作依據，包含新增、修改、刪除、搜尋。
漸漸的，PATCH就這麼地被遺忘了...XD


###### tags: `VueJS` `AXIOS` `JSON-Server`
