iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 24
1

文章留言功能的思路是我們讓使用者輸入自己的暱稱和留言內容,按下確定時會依照文章 id 將留言的字串上傳到所屬該文章的新集合中,我們的留言輸入位置在文章頁。

AritclePage.vue:

<template>
  <b-container fluid>
    <b-row>
      <b-col md="12">
        <div class="article">
          <div class="content__header">
            <h3>{{ articleInfo.title }}</h3>
            <p>文章作者: {{ articleInfo.authorInfo.displayName }}</p>
            <p>上傳時間: {{ articleInfo.contentData.createdAt }}</p>
          </div>
          <div class="article__content" v-html="articleInfo.contentData.html"></div>
        </div>
      </b-col>

      <b-col md="12">
        <b-card
            border-variant="secondary"
            header="討論板"
            header-border-variant="secondary"
            align="center"
          >
          <div v-for="comment in comments" :key="comment.id">{{ comment.who }} : {{ comment.value }}</div>
          <b-card-text v-if="!noComments">看起來目前乏人問津呢...</b-card-text>
        </b-card>
      </b-col>

      <b-col md="5">
        <b-card border-variant="info" header="我想說話..." align="center">
          <div>
            <b-form-input v-model="iAm" placeholder="我叫做..." autocomplete="off"></b-form-input>
          </div>
          <b-form-textarea
            autocomplete="off"
            id="textarea"
            v-model="commentTextarea"
            placeholder="Enter something..."
            rows="3"
            max-rows="6"
            class="mt-2"
          ></b-form-textarea>
          <b-button variant="light" class="mt-2" @click="leaveAMessage(commentTextarea)">留言</b-button>
        </b-card>
      </b-col>
    </b-row>
  </b-container>
</template>

<script>
import { db } from '../Model/FirebaseModel.js'
export default {
  name: 'ArticlePage',
  data () {
    return {
      articleId: this.$route.params.articleId,
      articleInfo: {
        aboutCategory: {
          tags: []
        },
        authorInfo: {
          displayName: '',
          email: '',
          uid: '',
          photoURL: ''
        },
        contentData: {
          title: '',
          createdAt: '',
          stopOnMore: '',
          value: '',
          html: ''
        }
      },

      comments: [],
      iAm: '',
      commentTextarea: ''
    }
  },
  computed: {
    noComments () {
      return this.comments.length
    }
  },
  created () {
    const self = this
    const articleRef = db.collection('posts').doc(this.articleId)
    articleRef.get().then((doc) => {
      if (doc.exists) {
        this.articleInfo = doc.data()
      } else {
        console.log('No such document!')
      }
    }).catch(function (error) {
      console.log('Error getting document:', error)
    })

    db.collection('posts').doc(this.articleId).collection('comments').orderBy('createdAt', 'desc')
      .get().then(function (querySnapshot) {
        querySnapshot.forEach(function (doc) {
        // doc.data() is never undefined for query doc snapshots
          console.log(doc.id, ' => ', doc.data(), ' => ', self.comments)
          self.comments.push(doc.data())
        })
      })
  },

  methods: {
    leaveAMessage () {
      const self = this
      const articleRef = db.collection('posts').doc(this.articleId).collection('comments')
      const d = new Date()

      let month = (d.getMonth() + 1)
      let date = d.getDate()
      let hours = d.getHours()
      let minutes = d.getMinutes()
      let seconds = d.getSeconds()
      if (String(date).length === 1) date = '0' + String(date)
      if (String(month).length === 1) month = '0' + String(month)
      if (hours === 0) hours = '00'
      if (String(hours).length === 1 === 0) hours = '0' + String(hours)
      if (String(minutes).length === 1) minutes = '0' + String(minutes)
      if (String(seconds).length === 1) seconds = '0' + String(seconds)
      const time = d.getFullYear() + '-' + month + '-' + date + '^^' + hours + ' : ' + minutes + ' : ' + seconds
      const comment = {
        who: this.iAm,
        value: this.commentTextarea + '-----' + time,
        createdAt: new Date().getTime()
      }

      articleRef.add(comment).then(function (commentData) {
        comment.id = commentData.id
        self.comments.push(comment)
      }).catch(err => {
        console.log(err)
      })
    }
  }
}
</script>

<style lang="scss" scoped>
.container-fluid {
  padding: 3rem;

  .card {
    margin-top: 2rem
  }
}
</style>

流程:

  1. 留言者輸入完暱稱和留言內容後,按下確定會執行 leaveAMessage,並且留言會在文章下新創見一個容器用來儲存留言,再上傳前可以看見我們有對時間做了一些基本的格式處理。
const articleRef = db.collection('posts').doc(this.articleId).collection('comments')
  1. 上傳完成後 push 到 data 內的 comments 陣列中,它代表的是一組組的留言資訊,已經用這樣的方式綁定,等每次載入到 created 階段就會對他進行 Firebase 的資料抓取,並把資料塞進 comments
<div v-for="comment in comments" :key="comment.id">{{ comment.who }} : {{ comment.value }}</div>

created () {
    const self = this
    const articleRef = db.collection('posts').doc(this.articleId)

		// 文章本體內容
    articleRef.get().then((doc) => {
      if (doc.exists) {
        this.articleInfo = doc.data()
      } else {
        console.log('No such document!')
      }
    }).catch(function (error) {
      console.log('Error getting document:', error)
    })

		// 抓留言下來塞進 comments
    db.collection('posts').doc(this.articleId).collection('comments').orderBy('createdAt', 'desc')
      .get().then(function (querySnapshot) {
        querySnapshot.forEach(function (doc) {
        // doc.data() is never undefined for query doc snapshots
          console.log(doc.id, ' => ', doc.data(), ' => ', self.comments)
          self.comments.push(doc.data())
        })
      })
  },

現在我們可以再次清空我們的數據庫,重新寫一篇文章,並且在文章底下留幾個言,而且重新整理的時候可以看見留言確實被讀取進來了。

https://ithelp.ithome.com.tw/upload/images/20201002/20129819BQ25ALLo9q.jpg

查看資料庫,確實有兩篇留言:

https://ithelp.ithome.com.tw/upload/images/20201002/20129819paTKVsiT3c.jpg

好了,文章的留言功能後面的思路就是這樣,把留言上傳到資料庫,在把資料庫上的留言抓下來顯示,很簡單ㄅ,
比較抱歉的是程式碼愈到後面愈沒有規劃,因為真的是比較忙的關係,都是想到什麼都先直接實作了,為了趕進度比較沒辦法好好整理再動手,後面的幾篇應該也會是這樣亂亂的,而且現在中秋連假在外面旅遊中,現在寫文都是抓空檔把筆電拿出來 PO 文的阿,還望各為體諒體諒,我等等還要想想下一篇要寫什麼呢,如果我前面沒出什麼疲漏的話,各位看到這邊代表了一件事,我目前手上的程式碼是和你們一模一樣的最新狀態,我的天哪我已經沒有扣打了拉~~~~


沒事也可以逛逛我們其他團隊成員的文章啦 ~~
eien_zheng: 前端小嘍嘍的Golang學習旅程_The journey of learning Golang 系列
PollyPO技術: 前端設計轉前端工程師-JS踩坑雜記 30 天 系列
阿電: 忍住不打牌位,只要30天VueJS帶你上A牌 系列
喬依司: 實作經典 JavaScript 30 系列


上一篇
Day 23: 中場休息,留言功能前置準備
下一篇
Day 25: 資料防護,編寫安全規則
系列文
Vue CLI + Firebase 雲端資料庫 30天打造簡易部落格及後臺管理30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言