iT邦幫忙

第 12 屆 iT 邦幫忙鐵人賽

DAY 29
0
Modern Web

30天手把手的vue.js教學!系列 第 29

2020it邦鐵人賽-30天手把手的Vue.js教學 Day29 - 關心時事! 做個簡單的COVID-19追蹤app吧!(中)

tags: Vue.js ItIron2020

前言

昨天我們完成了專案的基本建置並建立一個簡單的chart做為暖身練習,今天我們要接續chart的實作,從資料處理一直到呈現圖表,過程其實相當的簡單,甚至稱不上與vue有太大的關係XD 放輕鬆跟我來吧~! gogogo~!

資料處理

從昨天的LineChart示範我們知道要建立圖表必須將資料以陣列的方式傳進去,每個圖表都需要

  1. 日期(x軸)
  2. 人數統計(y軸)

兩個陣列陣列幫忙,我們最終需要產出六個圖表,由於日期會是共用的,所以最終我們需要六個變數儲存六組陣列,分別是

arrPositive: [],
arrHoptialized: [],
arrInIcu: [],
arrOnVentilators: [],
arrRecovered: [],
arrDeaths: []

我們首先要做的就是迭代我們利用axios拿到的資料,將我們所需的資料推進對應的陣列中! 在App.vue中,請你先引入dayjs

import dayjs from 'dayjs'

接著將data的部分新增以下的屬性

data() {
    return {
      arrPositive: [],
      arrHoptialized: [],
      arrInIcu: [],
      arrOnVentilators: [],
      arrRecovered: [],
      arrDeaths: [],
    }
}

並修改created的部分

async created() {
    // 利用解構的方式取出axios拿到的data
    let { data } = await axios.get(
      'https://api.covidtracking.com/v1/us/daily.json'
    )
    
    // 只取最近一個月的資料
    data = data.slice(0, 30)
    
    // 迭代陣列中的每個元素
    data.forEach((item) => {
      // 利用dayjs將原本的20201010改為2020/10/10
      const date = dayjs(`${item.date}`).format('YYYY/MM/DD')
      
      // 利用解構的方式取出data內我們需要的值
      const {
        positive,
        hospitalizedCurrently,
        inIcuCurrently,
        recovered,
        onVentilatorCurrently,
        death,
      } = item
      
      // 將值推進對應的陣列,每一個值都需要對應一個日期,所以要以物件的方式傳入
      this.arrPositive.push({ date, total: positive })
      this.arrHoptialized.push({ date, total: hospitalizedCurrently })
      this.arrDeaths.push({ date, total: death })
      this.arrRecovered.push({ date, total: recovered })
      this.arrOnVentilators.push({ date, total: onVentilatorCurrently })
      this.arrInIcu.push({ date, total: inIcuCurrently })
    })
  },

打開我們的vue-devtool檢查一下取得的資料,很好! 一切都如同我們所想

demo

建立確診人數圖表

現在我們已經有可用的資料了,馬上就先來打造確診人數的圖表吧! 我們需要在App.vue將我們整理好的資料傳入LineChart組件,因此我們要先回到LineChart組件做一些props的設定,我們要傳入的屬性有

  1. 該圖表的名稱(label)
  2. 我們整理後的圖表資料陣列(chartData)
  3. 圖表的額外設定(options)
  4. 圖表的顏色(chartColorOptions)

請你將LineChart的內容改為以下

<script>
import { Line } from 'vue-chartjs'

export default {
  extends: Line,
  
  // 加入基本的資料驗證
  props: {
    label: {
      type: String,
    },
    chartData: {
      type: Array,
    },
    options: {
      type: Object,
    },
    chartColorOptions: {
      type: Object,
    },
  },
  mounted() {
    // 從傳入的資料中取出數字與日期,並將其反轉(因為我們拿到的是最新到最舊的資料)
    const dates = this.chartData.map((d) => d.date).reverse()
    const totals = this.chartData.map((d) => d.total).reverse()

    this.renderChart(
      {
        labels: dates,
        datasets: [
          {
            label: this.label,
            data: totals,
            ...this.chartColorOptions
          },
        ],
      },
      this.options
    )
  },
}
</script>

處理完後我們回到App.vue,我們先在data內加入圖表選項,我們希望圖表響應式、根據畫面變化大小,更多的設定你一樣可以在官方文件中找到!

data() {
    return {
      arrPositive: [],
      arrHoptialized: [],
      arrInIcu: [],
      arrOnVentilators: [],
      arrRecovered: [],
      arrDeaths: [],
      chartOptions: {  // 新增這裡
        responsive: true,
        maintainAspectRatio: false,
      },
    }
  },

接著我們改寫一下我們的template,主要有著以下的改動

  1. 加入v-container讓兩側留白
  2. 利用v-row & v-col包裹我們的圖表,並加入v-if確認有資料後才render
  3. 傳入對應的資料給LineChart組件,其中包含顏色的設定(這部分目前很醜,我知道~!)

有關於vuetify的組件使用,都可以在官方文件上找到對應的說明,我這邊就不贅述囉!

<template>
  <v-app>
    <v-main>
      <v-container>
        <v-card>
          <v-container>
            <h1>COVID-19 Tracking Dashboard</h1>
            <v-row v-if="arrPositive.length">
              <v-col cols="12">
                <LineChart
                  label="Positive"
                  :chartData="arrPositive"
                  :options="chartOptions"
                  :chartColorOptions="{
                    borderColor: '#EF5350',
                    backgroundColor: 'rgba(255, 56, 96, 0.1)',
                  }"
                />
              </v-col>
            </v-row>
          </v-container>
        </v-card>
      </v-container>
    </v-main>
  </v-app>
</template>

順利的話你就會看到以下的畫面,我們真的弄好第一個圖表囉!

demo

利用computed整理要印出的資料

其他的圖表你自然可以如法炮製,直接複製6組一模一樣的程式碼,再修改傳入的資料即可

<v-row v-if="arrPositive.length">
    <v-col cols="12">
      <h2>{{ chartData.label }}</h2>
      <LineChart
        :chartData="chartData.data"
        :options="chartOptions"
        :label="chartData.label"
        :chartColorOptions="chartData.chartColorOptions"
      />
    </v-col>
</v-row>

這樣的東西複製貼上六次也是可以的!

不過做為一個文明人,DRY(don't repeat yourself)是個我們崇尚的基本原則! 當然要想想更好的方式來處理! 我們需要將所有要印出的資料整理成一個陣列,這個陣列會包含

  1. 要印出的圖表名稱
  2. 這圖表所要用到的資料
  3. 這個圖表的顏色

這樣最後我們只要透過一個v-for就能印出所有圖表囉!

請你在computed的部分增加一個renderData的屬性,並寫入以下的內容,對照著註解看我想會比較清楚我在幹什麼~!

computed: {
    renderData() {
      // 利用解構的方式從this中取出我們要用到的所有陣列
      const {
        arrPositive,
        arrHoptialized,
        arrInIcu,
        arrOnVentilators,
        arrRecovered,
        arrDeaths,
      } = this
      
      // 建立所有的labels
      const labels = [
        'Positive',
        'Hoptialized',
        'InIcu',
        'OnVentilators',
        'Recovered',
        'Deaths',
      ]
      
      // 將所有要輸出的陣列整理為一個,做為最終迭代的對象
      const displayedDataArr = [
        arrPositive,
        arrHoptialized,
        arrInIcu,
        arrOnVentilators,
        arrRecovered,
        arrDeaths,
      ]
      
      // 設置每個圖表的背景與邊界顏色
      const chartColorOptions = [
        {
          borderColor: '#EF5350',
          backgroundColor: 'rgba(255, 56, 96, 0.1)',
        },
        {
          borderColor: '#FFF176',
          backgroundColor: 'rgba(191, 182, 63, 0.1)',
        },
        {
          borderColor: '#FFB74D',
          backgroundColor: 'rgba(239, 109, 9, 0.1)',
        },
        {
          borderColor: '#B3E5FC',
          backgroundColor: 'rgba(9, 140, 239, 0.1)',
        },
        {
          borderColor: '#00E676',
          backgroundColor: 'rgba(11, 227, 47, 0.1)',
        },
        {
          borderColor: '#E30B86',
          backgroundColor: 'rgba(239, 9, 140, 0.1)',
        },
      ]
      
      // 最終整理成一個陣列,每一個元素都包含標籤名稱、資料以及圖表顏色的設定
      return displayedDataArr.map((data, index) => ({
        label: labels[index],
        data,
        chartColorOptions: chartColorOptions[index],
      }))
    },
  },

資料處理好後我們回到template的地方,做出以下的修改

<template>
  <v-app>
    <v-main>
      <v-container>
        <v-card>
          <v-container>
            <h1>COVID-19 Tracking Dashboard</h1>
            <v-row v-if="arrPositive.length">  // 修改以下的部分
              <v-col
                cols="12"
                v-for="chartData in renderData"
                :key="chartData.id"
              >
                <LineChart
                  :label="chartData.label"
                  :chartData="chartData.data"
                  :options="chartOptions"
                  :chartColorOptions="chartData.chartColorOptions"
                />
              </v-col>
            </v-row>
          </v-container>
        </v-card>
      </v-container>
    </v-main>
  </v-app>
</template>

很好! 畫面如我們預期的一樣,有著6個不同顏色、內容的圖表囉!

demo5

結語

今天我們成功的整理了利用axios取得的資料,並透過props傳遞給子層的LineChart讓它能正確顯示我們要的資料,最後則藉由新建立一個computed屬性進行v-for迭代,避免我們做重複的複製貼上~! 內容稍微多了一點,其中有些邏輯要配合註解去觀看會比較好懂! 基本上我們的應用程式已經有著該有的功能,明天我們只要做最後的一點點優化就可以上架囉! 那就明天見吧!

此文章同步發布於個人部落格,有興趣的大大也可以來參觀一下:D


上一篇
2020it邦鐵人賽-30天手把手的Vue.js教學 Day28 - 關心時事! 做個簡單的COVID-19追蹤app吧!(上)
下一篇
2020it邦鐵人賽-30天手把手的Vue.js教學 Day30 - 關心時事! 做個簡單的COVID-19追蹤app吧!(下)
系列文
30天手把手的vue.js教學!30

尚未有邦友留言

立即登入留言