前回の続き
ポストマンではなくfront側のフォームからrailsに送信できるように挑戦!
<Submit text="登録する" onClick={handleSubmit} />
この登録するボタンを押すと発火する関数に処理を書いていきます。
const [name, setName] = useState<string>("");
const [contactInfo, setContactInfo] = useState<string>("");
const token = useRecoilValue(tokenState);
const setErrorMessage = useSetRecoilState(errorMessageState);
const handleSubmit = async (e: FormEvent) => {
e.preventDefault();
const params = {
supplier: {
name: name,
contact_info: contactInfo,
},
};
try {
const res: AxiosResponse<Supplier> = await axios.post(
`${process.env.NEXT_PUBLIC_IP_ENDPOINT}/suppliers`,
params,
{ headers: { Authorization: `Bearer ${token}` } }
);
if (res.status === 201) {
console.log("success");
setName("");
setContactInfo("");
}
setErrorMessage(null);
} catch (error: AxiosError | any) {
console.log(error);
setErrorMessage(error.response.data.errors);
}
};
今回の注目点なのがparams
の部分です
const params = {
supplier: {
name: name,
contact_info: contactInfo,
},
};
ポストマンに送るリクエストボディに似てますねぇ〜!
さて、ここで
railsに送るパラメータを指定しています。
左辺が違うとストロングパラメータで弾かれて上手く送信できないため、しっかりとrailsのカラム名にすること!
右辺はfront側のstateの読み取り側です。
try,catch文でしっかりと成功した時とエラーが出たときの処理を書きます。
あとは次で紹介するセレクトボックスで作成した仕入れ先を選択して、
原材料を入力すればポストマンで登録した時と同じになります!
原材料登録の際の仕入れ先選択する
ではでは、次に原材料を登録する際に、仕入れ先idもユーザーが入力しなくてはなりません
この時に仕入れ先のidを入力して指定するのは、あまりにも使いにくいので、セレクトボックスを使うアプローチでいきます
こんな感じです↓
ですが仕入れ先のindexアクションのapiをそのまま使うのはあまりにも無駄なデータが多いため
セレクトボックス用のエンドポイント(api)を作成します。
まずはルーティング設定↓
get 'suppliers/select_index', to: 'suppliers#select_index'
resources :suppliers, only: %i[index show create update]
ここで注意するのが、
逆の順番で書くと、
suppliers/select_indexがshowアクション(suppliers/:id)とみなされてしまうため注意が必要です!!!
def select_index
suppliers = current_user.suppliers.select(:id, :name)
render json: { suppliers: }, status: :ok
end
indexアクションはもう使用されているのでカスタムルーティングを作成します。
(基本はrailsのレールに乗るということで基本の7アクションを使うのですが今回は追加で欲しいため仕方なく。。)
7アクションとは、index,show,create,update,destroyとrailsのみ場合で追加で、newとeditがあります。
コードの意味は簡単です!
selectメソッドで仕入れ先IDとその名前のみ取得。以上!!
それをjsonで返しているということです。
api/v1/suppliers/select_index
apiを叩くと、レスポンスボディ↓
{
"suppliers": [
{
"id": 3,
"name": "仕入れ先1"
},
{
"id": 4,
"name": "仕入れ先2"
},
{
"id": 5,
"name": "仕入れ先3"
},
{
"id": 6,
"name": "フロントから仕入れ先設定"
},
{
"id": 7,
"name": "上野商店"
},
{
"id": 8,
"name": "s"
},
{
"id": 9,
"name": "テスト仕入れ先"
},
{
"id": 10,
"name": "postmanから"
}
]
}
このように仕入れ先の情報がidと名前だけ取得に成功しました!
id1と2は別ユーザーが作ったものになるので取得できません!(しっかりとcurrent_userメソッドも息してます)
あとはfront側で設定してあげれば、その仕入れ先idを指定したということになります!
これは、セレクトボックスの中に表示させる仕入れ先一覧の例
// type.ts
export interface SupplierSelect {
id: number;
name: string;
}
// ingredidentForm.tsx
// ユーザーが選択した仕入れ先の情報を保持する
const [selectedSupplier, setSelectedSupplier] = useState<SupplierSelect | null>(null);
// apiから取得した仕入れ先のリストを保持する
const [suppliersList, setSuppliersList] = useState<SupplierSelect[]>([]);
// 仕入れ先のセレクトボックスのためのデータを取得する
useEffect(() => {
if (!token || !loaded) return;
const getSuppiersSelect = async () => {
try {
const res: AxiosResponse = await axios.get(
`${process.env.NEXT_PUBLIC_IP_ENDPOINT}/suppliers/select_index`,
{
headers: { Authorization: `Bearer ${token}` },
}
);
setSuppliersList(res.data.suppliers); // suppliersのリストをステートに設定
setSelectedSupplier(res.data.suppliers[0]); // 最初の仕入れ先を選択状態に設定
} catch (error: AxiosError | any) {
console.log(error);
setErrorMessage("仕入れ先の取得に失敗しました");
}
};
if (loaded) {
getSuppiersSelect();
}
}, [token, loaded, setErrorMessage]);
取得に成功したら
先ほど作成したAPIを叩いています、
${process.env.NEXT_PUBLIC_IP_ENDPOINT}/suppliers/select_index
,
ここで、リクエストヘッダーにtokenを設定して認証しています。
headers: { Authorization:
Bearer ${token} }
ここでAPIを叩いて得た情報を更新しています
setSuppliersList(res.data.suppliers);
ここで実際にユーザーが選んだ仕入れ先の情報を更新しています初期値は一番最初の仕入れ先です
setSelectedSupplier(res.data.suppliers[0]);
取得に失敗したら
setErrorMessage("仕入れ先の取得に失敗しました");
エラーメッセージを表示させます。
setErrorMessageという更新関数はRecoilで状態管理しています、後ほど説明します
railsにフォームで入力した値を送る
では、原材料に紐づけれるように仕入れ先をセレクトボックスで選択できるようにしました!
次はRailsにSubMitする関数を作ります。
handleSubMit関数です↓
const handleSubmit = async (e: FormEvent) => {
e.preventDefault(); // 実際はフォームを送信するとページをリロードしてしまうのですがそれを阻止しています
const params = {
ingredient: {
name: ingredientName,
buy_cost: Number(buyCost),
buy_quantity: Number(buyQuantity),
unit: unit,
supplier_id: selectedSupplier ? selectedSupplier.id : null,
},
};
try {
const res: AxiosResponse<Ingredient> = await axios.post(
`${process.env.NEXT_PUBLIC_IP_ENDPOINT}/ingredients`,
params,
{ headers: { Authorization: `Bearer ${token}` } }
);
if (res.status === 201) {
setName("");
setBuyCost("");
setBuyQuantity("");
setUnit("");
}
setErrorMessage(null);
} catch (error: AxiosError | any) {
console.log(error);
setErrorMessage(error.response.data.errors);
}
};
supplier_id: selectedSupplier ? selectedSupplier.id : null
このコードの意味は仕入れ先IDを登録し選択する際に、
万が一仕入れ先が登録されていない場合の
事を考えて、nullの可能性も考えつつ、選択された仕入れ先が存在するならその仕入れ先のIDを
supplier_id
に割り当てて、存在しない場合はnullとなります。
TypeScript使っているとnullの可能性がありますと、親切に教えてくれます
まあまず、Railsの検証とDB制約があるので保存はできませんが!!!
これにてnext.jsから原材料と仕入れ先を登録することが可能になりました!
セレクトボックスのコンポーネントについては説明していません
keyは必ず、key={supplier.id}
と設定することで、選択した仕入れ先のidが特定できます。
次は
Recoilについてまとめて見ました。