iT邦幫忙

2021 iThome 鐵人賽

DAY 29
0
Modern Web

前端?後端?你早晚都要全端的,何不從現在開始?系列 第 29

[Day 29] 實作-axios 串接api

先看一下有哪些api

下面列出這次用到的openapi,

可以看到他有一個共通的domain https://cloud.culture.tw/frontsite

然後後面分成 trans 跟 opendata,後續又根據功能不同拆成多個類別

節慶相關
https://cloud.culture.tw/frontsite/trans/SearchShowAction.do?method=doFindFestivalTypeJ

主題推薦
https://cloud.culture.tw/frontsite/trans/SearchShowAction.do?method=doFindIssueTypeJ

藝文活動-所有類別
https://cloud.culture.tw/frontsite/trans/SearchShowAction.do?method=doFindTypeJ&category=all

音樂表演資訊
https://cloud.culture.tw/frontsite/trans/SearchShowAction.do?method=doFindTypeJ&category=1

查詢單一活動詳細資料
https://cloud.culture.tw/frontsite/opendata/activityOpenDataJsonAction.do?method=doFindActivityById&id=5a64e3d8c6355e2a94b8a1cf

經緯度查詢附近未過期活動
https://cloud.culture.tw/frontsite/opendata/activityOpenDataJsonAction.do?method=doFindActivitiesNearBy&lat=25.051345&lon=121.549569&range=2

查詢單一類別未過期活動資料
https://cloud.culture.tw/frontsite/opendata/activityOpenDataJsonAction.do?method=doFindActivitiesByCategory&category=1

開始串接!

首先在src下建立一個 api資料夾,建立 api.js 檔。

import axios from 'axios';

// 建立一個axios實例
const publicService = axios.create({
  baseURL: 'https://cloud.culture.tw/frontsite', // url = base url + request url
});

時間有限所以先做節慶活動跟主題推薦這2隻api,

會更新主題推薦主頁、節慶活動主頁、節慶活動詳細頁面 3個頁面!


// 節慶活動 api
// eslint-disable-next-line import/prefer-default-export
export const getFestival = data => publicService.get('/trans/SearchShowAction.do?method=doFindFestivalTypeJ', data);

// 主題推薦 api
export const getTopic = data => publicService.get('/trans/SearchShowAction.do?method=doFindIssueTypeJ', data);

更新主題推薦的api

這頁比較簡單一點,在TopicPage裡,我把資料逐筆塞進Topic 子組件,沒有做其他操作

所以只要修改TopicPage的資料來源,Topic子組件就會自動吃到新的資料

<script>
import Topic from './Topic';
import { getTopic } from '../api/api'; // 從api.js import 主題推薦的api方法

export default {
  name: 'TopicPage',
  components: { Topic },
  data() {
    return {
      title: '',  // 原本這邊都是寫死的假資料,直接改成空值即可
      note: '',
      issue: [],
    };
  },
  methods: {
    getTopicData() { // 寫一個接資料的方法
      getTopic()     // 呼叫api方法
        .then((res) => {
          this.title = res.data.title;   // 把api回傳的值存到data的參數裡
          this.note = res.data.note;
          this.issue = res.data.issue;
        })
        .catch((err) => {
          // eslint-disable-next-line no-console
          console.log(err);
        });
    },
  },
  created() {
    this.getTopicData();  // 生命週期函數,在頁面初始化時要執行資料
  },
};
</script>

看看成果

成功! 如果有看過 Day21 的應該會發現Title跟下面的活動內容都更新了!

而且後續api資料更新,這頁的資料也會自動刷新~

https://ithelp.ithome.com.tw/upload/images/20211013/20140745yGEjMB3rpR.png


更新節慶專區的api

這頁跟主題推薦差不多,都是迴圈渲染組件,所以就不贅述啦

<script>
import Festival from './Festival';
import { getFestival } from '../api/api';

export default {
  name: 'FestivalPage',
  components: { Festival },
  data: () => ({
    date: null,
    form: {
      keyword: null,
      start: null,
      end: null,
      location: [],
    },
    locations: ['臺北市', '基隆市', '新北市', '宜蘭縣', '桃園市', '新竹市', '新竹縣', '苗栗縣', '臺中市', '彰化縣', '南投縣', '雲林縣', '嘉義縣', '嘉義市', '臺南市', '高雄市', '屏東縣', '澎湖縣', '花蓮縣', '台東縣', '金門縣', '連江縣'],
    festivalList: [],
  }),
  methods: {
    getFestivalData() {
      getFestival()
        .then((res) => {
          this.festivalList = res.data;
        })
        .catch((err) => {
          // eslint-disable-next-line no-console
          console.log(err);
        });
    },
  },
  created() {
    this.getFestivalData();
  },
};
</script>

看看成果

https://ithelp.ithome.com.tw/upload/images/20211013/20140745igJ1Kd2vsE.png


更新節慶專區詳細頁面

這頁就略為複雜了,他一樣是要用節慶專區的api,

但是拿到的是全部資料,還需要再從中篩選出頁面對應的那筆

跟上面2個一樣,把假資料改為空值,在create()的地方引入api方法

有更改的地方我會用註解標註

<script> 
import { getFestival } from '../api/api';  //引入api方法

export default {
  name: 'FestivalDetailPage',
  data: () => ({
    actId: 0,
    actData: {},
    festivalList: [],  // 把原本的假資料改為空值
  }),
  methods: {
	//獲取全部資料
    getAllData() { 
      getFestival()
        .then((res) => {
          this.festivalList = res.data; //先把全部資料存入data()
          this.getActData();           // 挑出這頁要用的那筆資料
        })
        .catch((err) => {
          // eslint-disable-next-line no-console
          console.log(err);
        });
    },
    getActData() {
      const actId = this.$route.query.actId;
      let selectedAct;
      this.festivalList.forEach((obj) => {
        if (obj.actId === parseInt(actId, 10)) {
          selectedAct = obj;
        }
      });
      this.actData = selectedAct;
    },
    getImageUrl(imageUrl) {
      return `https://cloud.culture.tw/${imageUrl}`;
    },
    getDateFormat(datetime) {
      const newDate = new Date(Date.parse(datetime));
      return `${newDate.getFullYear()}/${newDate.getMonth() + 1}/${newDate.getDate()}`;
    },
    getGrade() {
      const { grade1, grade2, grade3, grade4, grade5, grade6 } = this.actData;
      const gradeList = [grade1, grade2, grade3, grade4, grade5, grade6];
      let gradeResult = '';
      gradeList.forEach((grade) => {
        if (grade) {
          gradeResult += (`${grade} `);
        }
      });
      return gradeResult;
    },
  },
  created() {
    this.getAllData(); //在頁面初始化時戳api拿資料
  },
};
</script>

更改完後,頁面可以正常顯示了,但是好像有點小問題

節慶專區刷新時會跳error

錯誤訊息如下,可以看到本來應該帶imageUrl的地方,它卻拿到undefined,所以報錯

https://ithelp.ithome.com.tw/upload/images/20211013/20140745bz8EgPKU6S.png

雖然圖片顯示正常,但是有error我們還是要處理一下!

這個問題是因為我原本的資料是放在data()裡,

然後template裡用 getImageUrl() 去把圖片欄位換成正確的網址,轉換後才會渲染頁面

原本的寫法

<v-img :src='getImageUrl(actData.imageUrl)' />
getImageUrl(imageUrl) {
    return `https://cloud.culture.tw/${imageUrl}`;
},

那現在變成頁面初始化時,因為data的值我們改成空值了,getImageUrl時會拿不到資料 報錯,

然後才執行create() 裡的方法,去把api撈到的資料存到data裡

用文字講比較難理解,直接上圖!

原本的寫法在左邊,因為直接拿data的資料所以會正常顯示不報錯

改拿api資料後,他會先拿一次資料,執行完create()後又拿一次,所以頁面會正常顯示但是第一次失敗後會報錯誤訊息

https://ithelp.ithome.com.tw/upload/images/20211013/20140745PdYnZ9SLwR.png

修改程式碼

既然第二次拿的到資料,就代表第一次的拿資料是不必要的

只要保留讓api撈取後可以把資料存進data,再讓他渲染這段即可

https://ithelp.ithome.com.tw/upload/images/20211013/20140745nqF3CSaH5D.png

<!-- 改成直接拿data裡的資料 -->
<v-img :src='actData.imageUrl'/> 
// 移除getImageUrl(),直接在取得單筆資料時,更改image格式

getActData() {
      const actId = this.$route.query.actId;
      let selectedAct;
      this.festivalList.forEach((obj) => {
        if (obj.actId === parseInt(actId, 10)) {
          selectedAct = obj;
        }
      });
      this.actData = selectedAct;
      this.actData.imageUrl = `https://cloud.culture.tw/${this.actData.imageUrl}`;
    },

好惹~ api的部分就先講到這邊啦

後續其實還有挺多東西要做的,不過時間有限真的是講不完

29天了,明天來講一下目前的成果總結

快完賽啦 oh ya


上一篇
[Day 28] axios 這麼多API到底要放哪阿?
下一篇
[Day 30] 結算成果!鐵人賽真的結束了嗎?!
系列文
前端?後端?你早晚都要全端的,何不從現在開始?31

1 則留言

0
wolfwang
iT邦新手 2 級 ‧ 2021-10-15 15:42:39

加油加油!

我要留言

立即登入留言