第十九課:新篇章將api與clientSide網站UI介面連接
前一天我們了解到了axios,如何在介面與Api幫我們搭起了橋樑,今天我們要來繼續完成我們全端串連,將先從把原本的測試資料換成我們的資料庫資料,並來熟悉如何使用filter與req.query。
昨天成功的串接了第一次的Api到我們的UI介面,而這次我們要來打造新的專屬於我們feature各個區塊想要的Api,並最後我們會把昨天的Api也進行修改,因為昨天的Api是做抓全部的getAllhotels 的Api但到時候我們是要全部符合是pupolarHotel:true我們允許他是熱門住宿等條件。這些filter限制都會在今天內完成。
所以第一步我們先來做Api種類分析與統計。所以先回到我們的Api Folder。並我們知道feature的這些統計資料都是針對hotels的所以我們快速找到我們的ApiRoutes的hotels.js內與routesController裡面的hotel.js都可以先打開,因為我們Api創建要一次處理這兩個。
所以在實作前要先了解Api如果有filter等功能的ApiUrl會是怎麼處理
首先我們要先了解到req.query的用法,才能知道我們怎麼將hotel種類分類,在feature的第一欄"依住宿類型瀏覽"我們希望將所有的hotel分類為飯店、公寓、度假村等等的,我們的Url配合搜尋是type=飯店,公寓,度假村,Villa,木屋,小屋,豪華露營,飯店式公寓,度假屋,家庭旅館,青年旅館,
依照這些type去統計這些各個的數量。所以回到我們的Apifolder的routesController的hotel.js創建一個新的Api如下
ApiRoutes.js
//要來做"依住宿類型瀏覽"的種類資料統計與分析
router.get("/amountoftype", amountOfType)
RoutesController.js
//來統計各個type的種數
export const amountOfType = async(req,res,next)=>{
const type = req.query.type.split(",")
try{
const list= await Promise.all(type.map(type=>{
return Hotel.countDocuments({type:type})
}))
res.status(200).json(list)
} catch (error) {
next(errorMessage(500,"無法抓取住宿種類",error))
}
}
Promise.all之前講解過是說可以記成常用在把Array的重組上,並之後回傳我們要的數列,並我們就可以先去insomnia上實測。所以順序永遠都是Api寫好insomnia串接成功,我們到client利用axios去接上這個成功的Api。
insomnia url:
http://localhost:5000/api/v1/hotels/amountoftype?type=飯店,公寓,度假村,Villa,木屋,小屋,豪華露營,飯店式公寓,度假屋,家庭旅館,青年旅館
or
{{base_url}}/hotels/amountoftype?type=飯店,公寓,度假村,Villa,木屋,小屋,豪華露營,飯店式公寓,度假屋,家庭旅館,青年旅館
這邊抓到的[2,1,2,0,0,....]就是我們要的統計總數,快速完成其中一個Api我們也乾脆都把這邊都完成後再一起去client Side
所以一樣我們會利用到req.query的方式,來抓我們的各個城市數量
所以先一樣我們回到Api folder 專注在我們的hotel Api,
ApiRoutes.js
//要來做"依住宿城市瀏覽"的種類資料統計與分析
router.get("/amountofcities", amountOfCities)
RoutesController.js
//來統計各個cities的種數
export const amountOfCities = async(req,res,next)=>{
const cities = req.query.cities.split(",")
try{
const list= await Promise.all(cities.map(city=>{
return Hotel.countDocuments({city:city})
}))
res.status(200).json(list)
} catch (error) {
next(errorMessage(500,"無法統計各個城市的提供住宿的數量",error))
}
}
insomnia Url
http://localhost:5000/api/v1/hotels/amountofcities?cities=台南,台中,宜蘭縣,花蓮市,台東市,台北,高雄,墾丁,礁溪鄉,嘉義市
or
{{base_url}}/hotels/amountofcities?cities=台南,台中,宜蘭縣,花蓮市,台東市,台北,高雄,墾丁,礁溪鄉,嘉義市
這邊完成後我們就可以回到clientSide
這邊由於一開始並沒有設計的很好,導致我們會有重複出現useFetch的方式,但一樣可以解決,回歸到我們的component關係內,我們雖然只可以一個component內出現一個useFetch(這邊如果覺得邏輯太複雜有點多此一舉也可以直接使用axios.get就可以抓取Api意思一樣,但這邊還沒有利用到loading的數值,所以會對我們useFetch的功能有點匪夷所思,下面會利用到loading功能就會了解到為啥axios.get會需要特別拉出來一個fetchData來做。
feature.js
const typeUrl=`/hotels/amountoftype?type=${CategoriesType.map((type)=>type.name)}`
const citiesUrl=`/hotels/amountofcities?cities=${CategoriesCities.map((city)=>city.name)}`
console.log(typeUrl,citiesUrl)//可以利用clg來看測資url有沒有生成成功
並在下方的導入我們的url
<Categories dataArray={CategoriesType} url={typeUrl} />
<Categories dataArray={CategoriesCities} url={citiesUrl} />
並回到subcomponents內的Categories.jsx內把我們的useFetch導入並傳入正確的資料進去在 desc的div
import useFetch from '../hooks/useFetch'
import "./categories.scss"
const Categories = ({dataArray,url}) => {
const {data,loading,error} =useFetch(url)
return (
<div className='categories'>
{dataArray.map((item, index) =>
<div className="item" key={index}>
<img src={item.img} alt="" />
<div className="itemInfo">
<div className="title">
{item.name}
</div>
<div className="desc">
{`${data[index]}間住宿`}
</div>
</div>
</div>)}
</div>
)
}
到這邊後我們已經會filter特殊統計資料了,所以我們可以把popularHotel熱門住宿景點的Api完整,原本是全部都抓,但現在我們可以抓我們只想要的資料利用req.query,像是在url上有註解說只抓這個飯店是熱門的popularHotel:true,這邊我們可以在創建多一些資料來測試是否到時候只成功抓取popularHotel:true的資料而不抓popularHotel:false的資料,並這個可以使用在任何產品的filter與你想讓他登上熱門榜,你就可以去改動他的後端資料他就能上榜,在更多也可以自動以瀏覽數啊等其他資料來決定哪些資料是可以打開popularHotel:true等,產品排行榜應用。
所以我們先可以更改資料並回到Api folder處理來重新處理我們那時候連接的getAllHotel Api
這邊我們想要一次不用欄位對欄位的搜尋,也就不用每次我們都多一個條件我們Api那邊就要抓query.city之類的。
//getAllHotels升級版,讓他能抓取全部資料也能依照query值去找想要的資料
export const getAllHotels = async(req,res,next)=>{
const withQuery=req.query;
//如果url上有寫popularHotels=true,popularHotels會回傳true 沒有寫就沒這條件
try{
const hotelsList = await Hotel.find(
{
...withQuery //...代表說只要找到有相關欄位且符合的
}
).limit(7) //讓他回傳資料最多就七個
res.status(200).json(hotelsList)
}catch(error){
next(errorMessage(500,"無法抓取所有飯店資料",error))
}
}
所以查詢都需符合不同種條件可以用&來串接,但如果是像最上面查詢一個條件如city但會是一長串的array要重複查詢可以使用","來列陣。
成功後就能實現就像是搜尋欄的功能一樣,我們直接在url上查詢我們想要的資訊。
成功後代表我們可以回去client side的api串接,只要將我們的useFetch的Url加上我們想要的條件,我們就能調出想要的資料。回到feature folder
const {data,loading,error} =useFetch("/hotels?popularHotel=true")
這邊完成後,附上完整版
github.day19連結
今天我們完成了把測試資料換成資料庫的feature主體,同時新增了query的用法,之後到後台時,如需要大量的抓取資料,形式也會是走這種方式,將我們的get method採取query來抓取我們想要的資料,或是之後會講到的除了以url去搜尋特定項目的資料,我們也可以使用findOne等函數,讓想要搜尋的特定資料是可以以body去傳輸與查詢,使用url查詢的用意是在對付固定的物件filter,但當我們是要查變動的項目資料等等的,就會使用到後面的函數來處理,後面也會一次講解這種的用法。