# 前端

整體搜尋功能架構:

  • SearchBar - 前端搜尋欄位,捕捉使用者輸入。
  • HomePage - 用 useLocation 取得網址中的關鍵字,透過 useEffect 監聽關鍵字的變化,再透過 dispatch 來發送請求
  • productActions - 與後端 API 溝通,發送帶有 keyword 參數的請求
  • 後端 product_views - 根據傳入的關鍵字查詢資料庫,返回符合條件的商品清單

# Search Bar Component(搜尋欄)

  • 判斷若搜尋表單沒有 keyword,就停留在當前頁面 window.location.pathname
// SearchBar.js
const navigate = useNavigate();

const [keyword, setKeyword] = useState("");

const submitHandler = (e) => {
 e.preventDefault();
 if (keyword.trim()) {
  navigate(`/?keyword=${keyword}`); // 這邊 ?keyword 不用加任何其他東西,否則 HomePage 的 useLocation 印不出 keyword
 } else {
  navigate(window.location.pathname);
 }
};
return (
 <Form onSubmit={submitHandler} className='d-flex' style=<!--swig0-->>
  <Form.Control type='text' name='q' onChange={(e) => setKeyword(e.target.value)} placeholder='搜尋商品...' className='me-2 form-control-sm' />
  <Button type='submit' variant='outline-success' className='p-2'>
   <i className='fas fa-search'></i>
  </Button>
 </Form>
);

# HomePage.js(首頁)

  • 使用 useLocation 取得 route 資訊,這邊是取得 search 也就是網址中的 query string
let keyword = useLocation().search;
console.log(keyword);

useEffect(() => {
 dispatch(listProducts(keyword));
}, [dispatch, keyword]);

檢查主控台,可以看到 keyword 的值是 ?keyword=

console_keyword

# productActions.js(Redux Action)

傳入關鍵字的寫法很活,沒有一定

假設在 HomePage 中寫 dispatch(listProducts("")); // 明確傳入空字串,那在 listProducts 中就可以寫 keyword 即可,都一樣能運作

export const listProducts =
 (keyword = "") =>
 async (dispatch) => {
  try {
   dispatch({ type: PRODUCT_LIST_REQUEST });
   const { data } = await axios.get(`/api/products/${keyword}`);
   dispatch({
    type: PRODUCT_LIST_SUCCESS,
    payload: data, // reducers的action.payload
   }); // 成功的話
  } catch (error) {
   dispatch({
    type: PRODUCT_LIST_FAIL,
    payload: error.response && error.response.data.message ? error.response.data.message : error.message,
   }); // 檢查若 error.response && error.response.data.message 存在就回傳 error.response.data.message 否則就用預設的error.message
  }
 };

# 後端

# views(取得所有產品)

  • 將列出所有產品的程式放在 query 為 None 的情況 products = Product.objects.all() 避免 UnboundLocalError: local variable 'products' referenced before assignment 錯誤
  • icontains 不區分大小寫

可以先將 query 印出來看看,確認是否有正確接收到前端傳來的關鍵字

backend_query

@api_view(['GET'])
def getProducts(request):
    query = request.query_params.get('keyword')
    # print("query:", query)
    if query is None:
        query = ''
        products = Product.objects.all()
    else:
        products = Product.objects.filter(name__icontains=query)

    serializer = ProductSerializer(products, many=True)
    return Response(serializer.data)

最後成功結果,搜尋 a 只顯示產品名稱中有 a 的商品:

search_success