前一天的課程把前端與後端的重要功能按照順序開發完成,接下來我們來利用這個觀念,進行一些變化,算是更加進階一點的技術,而且整個開發的過程要從後端開始,一路開發到前端。
我相信對大家來說這個應該會是一個挑戰,但It鐵人賽就是來挑戰自我的地方,所以就讓我們來挑戰一下吧!
今天的功能,是開發根據類別篩選清單的功能,我們今天的目標是要開發完後端Spring boot的程式碼,就讓我們開始吧
依照前幾天的教學,在Spring boot的後端開發的順序是先從Repository->Serivce->ServiceImpl->Controller這個順序,而因為我們使用了JPA來當作Repository,JPA已經幫我們開發好很多的東西,我們就可以從Service開始
所以我們來到Service這個頁面中新增以下的功能
	List<String> getAllCategories();
再來到ServiceImpl層,我們這裡的功能是取得所有的類別,邏輯上是先讀取所有的資料,然後把資料做篩選,取得Category的資料回傳到前端,所以我們事先使用finall這個功能,然後用stream的方式把所有資料一筆筆取出來,用map的方式取得所有category,然後分區之後再用collect的方式取得資料。
	@Override
	public List<String> getAllCategories() {
	       List<Account> accounts = accountRepository.findAll();
	        return accounts.stream()
	                .map(Account::getCategory)
	                .distinct()
	                .collect(Collectors.toList());
	}
完成之後我們回到Controller,新增取得category的功能
    @GetMapping("/categories")
    public List<String> getAllCategories() {
        return accountsService.getAllCategories();
    }
再來就是來測試,只要回傳了你先前設置的清單即可
接下來,我們就要到前端繼續開發了
我們要再React Service中心層新增這個功能,我們這個頁面會先從後端中取得所有的類別,給使用者選取,再來根據選取到的資料丟回給Spring boot,從Spring boot中取得所有這個類別的資料,所以我們要新增getAllCategories跟getAccountsByCategory這兩個功能
export const getAllCategories = ()=>axios.get(BASE_REST_API_URL+'/categories')
export const getAccountsByCategory = (category)=>axios.get(BASE_REST_API_URL+'/category/'+category)
再來,我們就要新增篩選的頁面,我們要按照先前的邏輯,要引用useState, useEffect這些控制網頁參數與變動的功能,以及使用到我們開發的getAllCategories, getAccountsByCategory 兩個功能。
我們在這裡要使用到的參數有categories、selectedCategory跟最後從Spring取得的filteredAccounts
在網頁讀取的時候,我們就要先取得所有的類別,再把這些類別顯示到網頁中。
完整的程式碼如下
import React from 'react'
import { useState, useEffect } from 'react';
import { getAllCategories, getAccountsByCategory } from '../Service/AccountService';
const SortAccountComponent = () => {
    const [categories, setCategories] = useState([]);
    const [selectedCategory, setSelectedCategory] = useState('');
    const [filteredAccounts, setFilteredAccounts] = useState([]);
    // 取得所有的 Category
    useEffect(() => {
        getAllCategories().then((response) => {
            setCategories(response.data);
        }).catch((error) => {
            console.error(error);
        });
    }, []);
    // 當選擇的 Category 改變時,取得對應的帳戶清單
    useEffect(() => {
        if (selectedCategory) {
            getAccountsByCategory(selectedCategory).then((response) => {
                setFilteredAccounts(response.data);
            }).catch((error) => {
                console.error(error);
            });
        }
    }, [selectedCategory]);
  return (
    <div className='container'>
    <br></br>
    <div className='card col-md-6 offest-md-3 offset-md-3'>
        <br></br>
        <h2 className='text-center'>Sort Accounts by Category</h2>
        <div className='card-body'>
            <div className='form-group mb-2 text-center'>
                <label className='form-label'>Select Category:</label>
                <select
                    className='form-control'
                    value={selectedCategory}
                    onChange={(e) => setSelectedCategory(e.target.value)}
                >
                    <option value=''>Select a Category</option>
                    {categories.map((category, index) => (
                        <option key={index} value={category}>
                            {category}
                        </option>
                    ))}
                </select>
            </div>
            <br></br>
            {selectedCategory && (
                <div>
                    <h3 className='text-center'>Accounts in {selectedCategory}</h3>
                    <ul className='list-group'>
                        {filteredAccounts.map((account) => (
                            <li key={account.id} className='list-group-item'>
                                <strong>{account.name}</strong> - ${account.amount} ({account.expensed ? 'Expense' : 'Income'})
                            </li>
                        ))}
                    </ul>
                </div>
            )}
        </div>
    </div>
</div>
  )
}
export default SortAccountComponent
因為前後端整合是一件比較艱深的內容,所以我會預期大家先有html、CSS、JS的基礎,但這邊也還是多補充解釋一些前端網頁的呈現內容
                <select
                    className='form-control'
                    value={selectedCategory}
                    onChange={(e) => setSelectedCategory(e.target.value)}
                >
                    <option value=''>Select a Category</option>
                    {categories.map((category, index) => (
                        <option key={index} value={category}>
                            {category}
                        </option>
                    ))}
                </select>
這一段程式碼是顯示選擇的欄位,將讀取到的categories用category跟id的形式呈現,對外顯示類別
再來就是當我們選擇之後,就會出現一個selectCategory,那就會顯示以下的內容
 {selectedCategory && (
                <div>
                    <h3 className='text-center'>Accounts in {selectedCategory}</h3>
                    <ul className='list-group'>
                        {filteredAccounts.map((account) => (
                            <li key={account.id} className='list-group-item'>
                                <strong>{account.name}</strong> - ${account.amount} ({account.expensed ? 'Expense' : 'Income'})
這樣一來就能夠完成我們的目的,再來,我們要去App.jsx去設地這個頁面對應的網址
          {/* http://localhost:8080/sort-account/ */}
          <Route path='/sort-account' element={<SortAccountComponent />}></Route>
完整的程式碼如下
  return (
    <>
    <BrowserRouter>
        <HeaderComponent />
        <Routes>
        {/* http://localhost:8080/ */}
        <Route path='/' element={<ListAccountComponent />}></Route>
          {/* http://localhost:8080/weekly-account' */}
          <Route path='/week-account' element={<WeeklyAccountComponent />}></Route>
          {/* http://localhost:8080/list-account */}
          <Route path='/lsit-account' element={<ListAccountComponent />}></Route>
          {/* http://localhost:8080/add-account */}
          <Route path='/add-account' element={<AccountComponent />}></Route>
          {/* http://localhost:8080/update-account/1 */}
          <Route path='/update-account/:id' element={<AccountComponent />}></Route>
          {/* http://localhost:8080/sort-account/ */}
          <Route path='/sort-account' element={<SortAccountComponent />}></Route>
        </Routes>
        <FooterComponent />
    </BrowserRouter>
    </>
  )
結果如下
點選類別,然後隨便選擇一個類別
選擇好之後就會出現所有的清單
到這邊,恭喜你完成了這段進階的功能!