概要
下記でクエリパラメーターの値に伴って一覧に表示する項目を絞り込む処理を作成した。
今回は別画面で一覧に表示する条件を設定し、一覧画面に遷移させてみようと思う。
詳細
トップページに検索条件を選択するウインドウ的なものを用意し、検索条件を入力後「検索する」ボタンを押下するとクエリパラメーターが入った状態の一覧画面に遷移させたい。
前提
下記記事の内容が完了していること
方法
トップ画面/
から/tasks
に遷移するボタンの設置
まずは下記の方法でトップ画面からクエリパラメーター無しの/tasks
に遷移するボタンを設置する。
-
app/routes/_index.tsx
を下記のように修正し、ボタンを設置app/routes/_index.tsximport type { MetaFunction } from "@remix-run/cloudflare"; import { useNavigate } from "@remix-run/react"; export const meta: MetaFunction = () => { return [ { title: "Top" }, { name: "description", content: "Top", }, ]; }; export default function Index() { const navigate = useNavigate(); const handleSearchClick = () => { navigate("/tasks"); // 一覧画面に遷移 }; return ( <div style={{ fontFamily: "system-ui, sans-serif", lineHeight: "1.8" }}> <div> <h2>タスク管理</h2> <button onClick={handleSearchClick}>検索する</button> </div> </div> ); }
-
npm run dev
を行い、トップ画面にアクセスし「登録する」をクリックし、クエリパラメーターがついていない一覧が表示されることを確認 -
app/routes/_index.tsx
を下記のように修正し、一旦クエリパラメーターをハードコーディングapp/routes/_index.tsximport type { MetaFunction } from "@remix-run/cloudflare"; import { useNavigate } from "@remix-run/react"; export const meta: MetaFunction = () => { return [ { title: "Top" }, { name: "description", content: "Top", }, ]; }; export default function Index() { const navigate = useNavigate(); const handleSearchClick = () => { navigate("/tasks?isDeleted=true"); // 遷移先ページ指定 }; return ( <div style={{ fontFamily: "system-ui, sans-serif", lineHeight: "1.8" }}> <div> <h2>タスク管理</h2> <button onClick={handleSearchClick}>検索する</button> </div> </div> ); }
-
トップ画面にアクセスし「登録する」をクリックし、
isDeleted=true
クエリパラメーターがついている一覧が表示されることを確認
絞り込み内容を入力する場所の追加
一気に複数項目の絞り込みメニューを作ると複雑になりそうなのでまずはisDeletedだけを絞り込むフィールドを追加してみる。
-
app/routes/_index.tsx
を下記のように修正し、削除済みタスクだけを絞り込むフィールドを追加app/routes/_index.tsximport type { MetaFunction } from "@remix-run/cloudflare"; import { useNavigate } from "@remix-run/react"; import { useState } from "react"; export const meta: MetaFunction = () => { return [ { title: "Top" }, { name: "description", content: "Top", }, ]; }; export default function Index() { const navigate = useNavigate(); const [isDeleted, setIsDeleted] = useState(false); const handleSearchClick = () => { const queryParams = new URLSearchParams({ isDeleted: isDeleted.toString() }); navigate(`/tasks?${queryParams.toString()}`); // クエリパラメーターを含めて遷移 }; return ( <div style={{ fontFamily: "system-ui, sans-serif", lineHeight: "1.8" }}> <div> <h2>タスク管理</h2> <div> <label> <input type="checkbox" checked={isDeleted} onChange={(e) => setIsDeleted(e.target.checked)} /> 削除済みを表示 </label> </div> <button onClick={handleSearchClick}>検索する</button> </div> </div> ); }
-
トップ画面にアクセスし、「削除済みを表示」のチェックボックスをチェックつけたり、外したりして「登録する」をクリックし、
isDeleted=true
クエリパラメーターのtrueやfalseが設定されて、一覧が表示されることを確認 -
今は「チェックボックスがONでもOFFでも、必ずクエリパラメーターがtrueかfalseで付与」されるが
app/routes/_index.tsx
を下記のように修正することで「チェックボックスがONの場合だけ、クエリパラメーターのtrueを付与」というように実装が可能。要件にもよるがURLがシンプルになるので筆者はこちらを選択(表示内容は変化しないようにtasks-loader.tsを記載しているので変わらない)(URLがシンプルになる分、_index.tsxのコードが若干複雑になる)app/routes/_index.tsximport type { MetaFunction } from "@remix-run/cloudflare"; import { useNavigate } from "@remix-run/react"; import { useState } from "react"; export const meta: MetaFunction = () => { return [ { title: "Top" }, { name: "description", content: "Top", }, ]; }; export default function Index() { const navigate = useNavigate(); const [isDeleted, setIsDeleted] = useState(false); const handleSearchClick = () => { const queryParams = new URLSearchParams(); if (isDeleted) { queryParams.append("isDeleted", isDeleted.toString()); } navigate(`/tasks?${queryParams.toString()}`); // クエリパラメーターを含めて遷移 }; return ( <div style={{ fontFamily: "system-ui, sans-serif", lineHeight: "1.8" }}> <div> <h2>タスク管理</h2> <div> <label> <input type="checkbox" checked={isDeleted} onChange={(e) => setIsDeleted(e.target.checked)} /> 削除済みを表示 </label> </div> <button onClick={handleSearchClick}>検索する</button> </div> </div> ); }
-
下記のような挙動になる。「チェックボックスつけたときだけクエリパラメーターが付与」されているところに注目
「完了可否」の検索条件の入力フィールドを追加
-
app/routes/_index.tsx
を下記のように修正し、「完了可否」の検索条件入力フィールドとクエリパラメーターの作成部分を追加app/routes/_index.tsximport type { MetaFunction } from "@remix-run/cloudflare"; import { useNavigate } from "@remix-run/react"; import { useState } from "react"; export const meta: MetaFunction = () => { return [ { title: "Top" }, { name: "description", content: "Top", }, ]; }; export default function Index() { const navigate = useNavigate(); const [isDeleted, setIsDeleted] = useState(false); const [isComplete, setIsComplete] = useState(false); const handleSearchClick = () => { const queryParams = new URLSearchParams(); if (isDeleted) { queryParams.append("isDeleted", isDeleted.toString()); } if (isComplete) { queryParams.append("isComplete", isComplete.toString()); } navigate(`/tasks?${queryParams.toString()}`); // クエリパラメーターを含めて遷移 }; return ( <div style={{ fontFamily: "system-ui, sans-serif", lineHeight: "1.8" }}> <div> <h2>タスク管理</h2> <div> <label> <input type="checkbox" checked={isDeleted} onChange={(e) => setIsDeleted(e.target.checked)} /> 削除済みを表示 </label> </div> <div> <label> <input type="checkbox" checked={isComplete} onChange={(e) => setIsComplete(e.target.checked)} /> 完了済みを表示 </label> </div> <button onClick={handleSearchClick}>検索する</button> </div> </div> ); }
-
動作確認は最後にまとめて実施予定
「タスクカテゴリ」の検索条件の入力フィールドを追加
「タスクカテゴリ」は複数項目存在するのでプルダウンで入力フィールドを用意する
-
app/routes/_index.tsx
を下記のように修正し、「タスクカテゴリ」の検索条件入力フィールドとクエリパラメーターの作成部分を追加app/routes/_index.tsximport type { MetaFunction } from "@remix-run/cloudflare"; import { useNavigate } from "@remix-run/react"; import { useState } from "react"; export const meta: MetaFunction = () => { return [ { title: "Top" }, { name: "description", content: "Top", }, ]; }; export default function Index() { const navigate = useNavigate(); const [isDeleted, setIsDeleted] = useState(false); const [isComplete, setIsComplete] = useState(false); const [taskCategoryMasterId, setTaskCategoryMasterId] = useState(""); // 初期値を空文字列として定義 const handleSearchClick = () => { const queryParams = new URLSearchParams(); if (isDeleted) { queryParams.append("isDeleted", isDeleted.toString()); } if (isComplete) { queryParams.append("isComplete", isComplete.toString()); } if (taskCategoryMasterId !== "") { queryParams.append("taskCategoryMasterId", taskCategoryMasterId.toString()); } navigate(`/tasks?${queryParams.toString()}`); // クエリパラメーターを含めて遷移 }; return ( <div style={{ fontFamily: "system-ui, sans-serif", lineHeight: "1.8" }}> <div> <h2>タスク管理</h2> <div> <label> <input type="checkbox" checked={isDeleted} onChange={(e) => setIsDeleted(e.target.checked)} /> 削除済みを表示 </label> </div> <div> <label> <input type="checkbox" checked={isComplete} onChange={(e) => setIsComplete(e.target.checked)} /> 完了済みを表示 </label> </div> <div> <label> タスクカテゴリ: <select onChange={(e) => setTaskCategoryMasterId(e.target.value)} defaultValue="" > <option value="" disabled>選択してください</option> <option value="1">仕事</option> <option value="2">勉強</option> <option value="3">その他</option> </select> </label> </div> <button onClick={handleSearchClick}>検索する</button> </div> </div> ); }
-
これだと「タスクカテゴリが仕事 かつ 勉強」という設定ができないが、それは次の記事で実装予定
ココまでの動作確認
-
トップを開き「完了済みを表示」にチェックを入れてクエリパラメーターが正常に入ること、一覧表示が正常なことを確認
-
トップを開き「タスクカテゴリ」を選びクエリパラメーターが正常に入ること、一覧表示が正常なことを確認
-
最後に複合的な条件で検索を行い、クエリパラメーターが問題なく表示されれば一旦ココまでは完了
タスクカテゴリプルダウンをテーブルから取得した情報で表示する
現状、タスクカテゴリのプルダウンの要素はハードコーディングされている。これだとtasks_category_mastersテーブルに要素を追加してもこのプルダウンの要素は増えない。
そのためプルダウンの要素をtasks_category_mastersテーブルから取得した要素を表示するように変更する。
-
app/routes/task-category-masters-loader.ts
のファイルを作成し下記のように記載app/routes/task-category-masters-loader.tsimport type { LoaderFunctionArgs} from "@remix-run/cloudflare"; export const loader = async ({ context }: LoaderFunctionArgs) => { try { return await context.db.task_category_masters.findMany(); } catch (error) { console.error("Failed to load users:", error); throw new Response("Internal Server Error", { status: 500 }); } }
-
app/routes/_index.tsx
を下記のように修正app/routes/_index.tsximport type { MetaFunction } from "@remix-run/cloudflare"; import { useNavigate, useLoaderData } from "@remix-run/react"; import { useState } from "react"; import { loader } from "./task-category-masters-loader"; export { loader }; export const meta: MetaFunction = () => { return [ { title: "Top" }, { name: "description", content: "Top", }, ]; }; export default function Index() { const navigate = useNavigate(); const [isDeleted, setIsDeleted] = useState(false); const [isComplete, setIsComplete] = useState(false); const [taskCategoryMasterId, setTaskCategoryMasterId] = useState(""); // 初期値を空文字列として定義 const handleSearchClick = () => { const queryParams = new URLSearchParams(); if (isDeleted) { queryParams.append("isDeleted", isDeleted.toString()); } if (isComplete) { queryParams.append("isComplete", isComplete.toString()); } if (taskCategoryMasterId !== "") { queryParams.append("taskCategoryMasterId", taskCategoryMasterId.toString()); } navigate(`/tasks?${queryParams.toString()}`); // クエリパラメーターを含めて遷移 }; interface TaskCategoryMaster { id: string; name: string; } const taskCategoryMasters = useLoaderData<TaskCategoryMaster[]>(); return ( <div style={{ fontFamily: "system-ui, sans-serif", lineHeight: "1.8" }}> <div> <h2>タスク管理</h2> <div> <label> <input type="checkbox" checked={isDeleted} onChange={(e) => setIsDeleted(e.target.checked)} /> 削除済みを表示 </label> </div> <div> <label> <input type="checkbox" checked={isComplete} onChange={(e) => setIsComplete(e.target.checked)} /> 完了済みを表示 </label> </div> <div> <label> タスクカテゴリ: <select onChange={(e) => setTaskCategoryMasterId(e.target.value)} defaultValue="" > <option value="" disabled>選択してください</option> {taskCategoryMasters.map((taskCategoryMaster) => ( <option key={taskCategoryMaster.id} value={taskCategoryMaster.id}> {taskCategoryMaster.name} </option> ))} </select> </label> </div> <button onClick={handleSearchClick}>検索する</button> </div> </div> ); }
-
下記のようにプルダウンのnameの値がtask_category_mastersのnameの値で設定されていることを確認