Rails × Stimulusを使ってフォームを送信するときに、フォームの内容が動的になっているのか、それをクライアント側で制御が必要な場合にどのようにフォームリクエストを送信するのが良いか考えることがあったのでまとめてみる。
具体例
Clientのデータを更新する処理を例に考えてみます。
(*targets, values, 関数定義など省略しています)
JSで値を上書きしてJSONリクエストするパターン
JSでしていることは、大きく以下5点です。
- request先(url)を保持してrequest
- request bodyを設定
- 更新後の通知バーの表示
- 更新に成功後のreload
- リクエスト
flash message
はredirect
後に表示されるので、JSからrequestすることでやや不自然な通知バー表示になってしまいます。(通知バーの表示 ⇛ 画面の再描画の順になっている)
const url = `/clients/{this.clientIdValue}`;
const options = {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': csrfToken(),
},
body: JSON.stringify({
client: { state: this.stateValue }
})
};
let response;
try {
response = await fetch(url, options);
} catch (e) {
$.notifyBar({ cssClass: 'error', html: '更新に失敗しました。', delay: 2500 });
return;
}
if (response.ok) {
$.notifyBar({ cssClass: 'success', html: '更新に成功しました。', delay: 2500 });
window.location.reload();
} else {
$.notifyBar({ cssClass: 'error', html: '更新に失敗しました。', delay: 2500 });
}
JSでフォームを生成してリクエストするパターン
JSでしていることは、大きく以下4点です。
- request先(url)を保持してrequest
- Formの生成
- Formの生成時にstateを設定
- リクエスト
わざわざFormを生成しており、メンテナンス性なども考えるとあまり良い実装とは言えなさそうです。
const url = `/clients/${this.clientIdValue}`;
const formData = new FormData();
formData.append('_method', 'PUT');
formData.append('authenticity_token', csrfToken());
formData.append('client[state]', this.stateValue);
try {
// フォーム送信として送る
window.location.href = url + '?' + new URLSearchParams(formData).toString();
} catch (e) {
...
}
JSで値を上書きしてViewからリクエストするパターン
JSでしていることは、大きく以下2点です。
- request先(url)を保持してrequest
- stateを設定
requestはview側のformから行います。
更新後の通知はバックエンド側でflash message
を設定すれば表示できるはずです。また、画面の再描画もredirect
させればできるのでJSでする必要がなくなりました。
const clientId = this.clientIdValue;
const state = this.stateValue;
const form = this.formTarget;
form.action = `/clients/${clientId}`;
this.stateTarget.value = state;
Ghost Formパターン
最後にこちらで紹介されているGhost Formパターンです。
Ghost Formパターンを今回のケースに当てはめてみます。
JSでしていることは、大きく以下3点です。
- Formを取得
- 値を設定
- 取得したFormからリクエスト
本編で語られているユースケースとはことなりますが、Ghost Formパターンにすることで、JS側とHTML、サーバ側の処理を分離できました。
フォーム画面の制御にまつわるロジックをサーバ側に集約できること
「インタラクティブな画面づくり」という課題を、サーバ側でRubyを用いて一元的に解くこと
Ghost Formパターンを利用することで責務が分離され、DRYでメンテナンス性向上の利点があるようです。
const formData = new FormData(this.originalFormTarget)
formData.delete("_method")
formData.delete("authenticity_token")
const input = this.ghostFormTarget.querySelector('input[name=state]')
if (input) {
input.value = this.stateValue;
}
this.ghostFormTarget.requestSubmit()
まとめ
Stimulusを使ったFormのリクエスト方法を検討してみました。
他にも実装方法はあるかと思いますが、JSではあまり複雑なことはせず、JS側とHTML、サーバ側の責務を分離して実装できると良さそうです。
また、Ghost Formパターンは今後利用していきたいなと感じました!!