iT邦幫忙

2026 iThome 鐵人賽

DAY 17
0
JavaScript

從看不懂到做出來,用 PawPal 走過前端新手村系列 第 17

Day 17|第一次做醫院評論,留言送出後星等怎麼跟著變?

  • 分享至 

  • xImage
  •  

今天的故事

在做 PawPal 的醫院功能時,我一直覺得,只有地圖和醫院基本資料還不太夠。

既然我們想做的是一站式的寵物照護網站,那我會希望使用者找醫院的時候,不用看完位置和基本資料之後,又跑到其他網站查評價。

所以我後來有一個想法:

要做的話,就把資訊盡量做完整一點。

除了醫院位置和基本資料之外,也加入評論和星等,讓使用者可以直接在 PawPal 裡看其他人的評價,也能留下自己的使用經驗。

這個功能後來也是由我負責實作。

一開始看起來其實很單純:

五顆星
+
一個留言框
+
一顆送出按鈕

但真的開始做之後,我才發現,畫面上看起來這麼簡單的一個功能,背後其實連著一整條資料流程。


我先在五顆星上遇到一個小問題

我印象很深,第一次把五星評分的互動做出來時,我點到第 4 顆星,前面的星星沒有像我預期的一起亮。

正常來說,選擇 4 星應該要看到:

★ ★ ★ ★ ☆

這個看起來很小的問題,反而讓我開始比較清楚地理解:

五星評分不是五個彼此獨立的按鈕。

使用者真正選擇的,其實是一個:

1 ~ 5

之間的評分數值。

畫面上的五顆星,只是把這個數字用比較直覺的方式呈現出來。

例如:

rating = 4

代表的就是:

第 1 顆 → 亮
第 2 顆 → 亮
第 3 顆 → 亮
第 4 顆 → 亮
第 5 顆 → 不亮

所以真正的判斷不是:

我剛才點的是哪一顆?

而比較像:

這一顆的位置,有沒有包含在目前選擇的星數裡?


五顆星,其實是在顯示同一個 rating

在 PawPal 的實作裡,會先記住使用者目前選擇的評分。

例如:

selectedRating = 4

畫面再依照目前的 rating,決定每一顆星是不是要呈現選取狀態。

下面是簡化概念

const activeRating = hoverRating || selectedRating

const isActive = activeRating >= star

以上為簡化概念,用來說明五星評分的主要判斷方式,不是 PawPal Repository 的完整原始程式碼。

這裡還多了一個 hoverRating

當使用者把滑鼠移到第 5 顆星時,可以先看到五顆星亮起來;如果沒有真的點下去,滑鼠移開後又會回到原本選擇的星數。

以前看到這種評分元件時,我只會看到:

「五顆可以點的星星。」

自己做過一次之後,我才比較明確地理解:

真正被程式保存的是 rating,星星只是把 rating 顯示出來。


但真正的主角,其實是「送出評論」之後

五星互動做好之後,接下來才是這個功能真正比較完整的部分。

使用者最後送出去的資料主要是:

rating
+
comment

例如:

4 星
+
「醫生很有耐心」

這裡只是簡單示意一筆評論可能包含的內容。

按下送出之後,事情並不是把這段文字加到畫面上就結束了。

整個流程其實比較像:

使用者選擇星等
↓
輸入評論
↓
送出 rating + comment
↓
POST 評論 API
↓
後端建立評論
↓
重新計算平均星等
↓
重新計算評論數
↓
API 回傳最新統計
↓
前端更新醫院資料
↓
重新取得評論列表
↓
畫面顯示最新結果

在開始實作以前,我本來就知道:

多了一則評論之後,星等和評論數量也應該一起更新。

但知道這條流程應該怎麼運作,和真的自己把它接起來,感覺還是不太一樣。

做到這裡時,我才比較有:

「原來一個評論功能真的會牽到這麼多地方。」

的感覺。


平均星等不是前端自己算

先用一個簡化概念來看。

假設現在一間醫院有三則評論:

5 星
4 星
3 星

平均星等就是:

(5 + 4 + 3) ÷ 3
= 4

所以:

平均星等:4
評論數量:3

以上數字為簡化概念,只是用來說明平均星等的計算方式,不代表 PawPal 的真實評論資料。

PawPal 並不是讓前端把全部評論抓回來,再自己把 rating 加總後計算平均。

這件事情是交給後端與資料庫處理。

主要會用到:

AVG(rating)
COUNT(*)

可以先簡單理解成:

AVG(rating)
→ 算出這間醫院目前所有評分的平均

COUNT(*)
→ 算出目前有幾則評論

實際上平均星等還會處理到小數一位。

所以當新的評論成功建立後,後端除了建立那一筆評論,也會重新取得最新的:

average_rating
review_count

再一起回傳給前端。

對我來說,這裡比較有意思的是:

前端不用自己負責重新計算平均值,而是接收後端整理好的最新統計結果,再把它更新到畫面上。


POST 成功後,畫面也要跟著變

如果事情只做到資料庫新增成功,其實還不夠。

假設使用者原本看到:

4.2 星
10 則評論

現在又新增了一則評論。

資料庫可能已經變成最新狀態了,但如果畫面還顯示:

4.2 星
10 則評論

使用者看到的還是舊資料。

所以評論新增成功之後,前端還需要把最新的結果同步回畫面。

在 PawPal 裡,POST 評論成功後會取得最新的 summary,再用這份資料更新目前醫院的:

rating
reviewCount

而這裡還有一個我實作時需要注意的地方。

醫院不只會出現在列表,也會出現在地圖相關的資料裡。

所以不能只更新其中一邊。

實際 Store 裡會同步處理:

hospitals
mapHospitals

這樣同一間醫院的評分資訊,才不會一邊已經更新,另一邊還停在舊資料。


那我剛剛寫的留言呢?

平均星等和評論數更新之後,還有一個很直覺的問題:

我剛剛送出去的評論,要不要馬上看得到?

答案當然是要。

所以 PawPal 在評論送出成功後,還會把評論畫面切回列表,重新取得一次最新的評論資料。

流程可以整理成:

POST 評論成功
↓
取得最新 summary
↓
更新平均星等與評論數
↓
切回評論列表
↓
重新 GET 最新評論
↓
更新 reviewList
↓
顯示最新評論

也就是使用者送出一則評論之後,看到的不只是:

「送出成功。」

而是真的能看到:

自己的評論出現在列表
平均星等更新
評論數量更新

這樣整個操作才算真正接起來。


一則評論,其實同時影響很多地方

做到這裡之後,我才比較有感地看到一件事情。

使用者表面上只是做了一個動作:

送出評論

但背後實際發生的是:

rating + comment
↓
API
↓
新增評論資料
↓
重新計算平均星等
↓
重新計算評論數
↓
回傳最新資料
↓
更新 Store
↓
重新取得評論列表
↓
更新畫面

以前剛開始學前端的時候,我很容易把功能想成:

「畫面做出來,而且按鈕可以按。」

但做完這次評論功能後,我開始比較會把功能想成一整條流程:

使用者做了什麼?
↓
前端送出什麼?
↓
後端收到什麼?
↓
資料庫怎麼改變?
↓
後端回傳什麼?
↓
前端最後有哪些地方需要更新?

這也是這次評論功能讓我最有感的地方。

不是哪一段程式碼特別厲害。

而是我第一次比較完整地看到:

一個看起來很普通的功能,是怎麼從前端一路跑到後端、資料庫,再回到使用者看到的畫面。


做完之後,醫院頁面真的完整很多

回頭看一開始為什麼想加入評論和星等,其實理由還是很單純。

我希望使用者來 PawPal 找醫院時,可以直接完成:

找醫院
↓
看醫院基本資料
↓
看星等
↓
閱讀評論
↓
留下自己的評價

而不是查完一家醫院之後,還要再另外開其他網站找評價。

當評論功能真的完成之後,我確實覺得醫院頁面更接近我一開始想要的樣子。

在同一個網站裡,就能把需要的資訊盡量一次看完。

對使用者來說更方便。

而對我來說,也第一次真的把一個看似簡單的功能,從畫面一路接成一條完整的前後端資料流程。


本篇重點與心得

這次做醫院評論與星等,我最有感的有幾件事情:

  1. 五星評分真正保存的是 1~5 的 rating,五顆星只是把這個數值視覺化。
  2. 一則評論新增後,影響的不只是評論列表,平均星等和評論數也需要一起更新。
  3. 資料庫可以透過 AVG()COUNT() 幫忙完成評分統計,再把結果交給前端。
  4. 一個功能真正完成,不只是 API 成功,而是最新結果也要正確回到使用者看到的畫面。

如果是以前的我,看到五星評論,看到的可能只是:

五顆星
+
一個留言框

但做完之後,我看到的已經變成:

一條從畫面走到資料庫,再回到畫面的完整流程。


下一篇預告

這次我做的是醫院評論。

看起來只是五星和留言,但真正做下去之後,才發現背後牽著前端、後端、資料庫和畫面同步。

而下一個功能,畫面上看起來可能更簡單。

只是一顆「收藏」按鈕。

但當它真的需要記住「哪一個使用者收藏了哪一間醫院」時,就不可能只改一個按鈕的樣式。

下一篇:

Day 18|一顆收藏按鈕,背後其實不只有前端


上一篇
Day 16|功能明明有反應,為什麼手機用起來還是不順?
下一篇
Day 18|一顆收藏按鈕,背後其實不只有前端
系列文
從看不懂到做出來,用 PawPal 走過前端新手村20
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言