Madogiwa Blog

主に技術系の学習メモに使っていきます。

Vue.js: CSRFTokenの設定やmethodを仕込むRailsの`form_with`っぽいComponentを作るメモ📝

Ruby on Railsで実装していたViewをVue.jsのComponentに置き換える場合に、form_withはよしなに行ってくれていたCSRF Tokenの設定やPATCH等のGETPOST以外のmethodをRailsに認識させるためにhiddenで送信したりする処理を独自に実装する必要があります。

form_with options

  • :method - The method to use when submitting the form, usually either “get” or “post”. If “patch”, “put”, “delete”, or another verb is used, a hidden input named _method is added to simulate the verb over post.
  • :authenticity_token - Authenticity token to use in the form.

https://api.rubyonrails.org/v7.0/classes/ActionView/Helpers/FormHelper.html#method-i-form_with

毎回formを作る度に実装するのは手間なのでいい感じに設定するComponentを作ってみたのでメモ📝

CSRFTokenの設定やmethodを仕込むRailsform_withっぽいComponentの実装

以下が実装してみたComponentです。propsで受け取ったrequestMethodGET以外だったらPOSTとして送信し、Railsに認識させるためにhiddenで元のrequestMethod(PATCH等)を送信するようにしているのと、 rails/ujscsrfToken相当の処理で取得したTokenをhiddenで仕込んでいます。

<template>
  <form ref="form" class="common-form" :action="requestPath" accept-charset="UTF-8" :method="formMethod">
    <input type="hidden" name="_method" :value="requestMethod" autocomplete="off" />
    <input type="hidden" name="authenticity_token" :value="authenticityToken" autocomplete="off" />
    <slot />
  </form>
</template>
<script setup lang="ts">
import { computed, ref } from "vue";
type Props = {
  requestMethod: "get" | "post" | "patch" | "put" | "delete";
  requestPath: string;
};
// NOTE: rails/ujs の csrfTokenの処理をコピペしている
// https://github.com/rails/rails/blob/ccb646244ff3768f796a5d9e0d22b833df3a6af6/actionview/app/assets/javascripts/rails-ujs.js#L47-L50
const csrfToken = () => {
  const meta = document.querySelector("meta[name=csrf-token]");
  return meta && (meta as HTMLMetaElement).content;
};
const props = defineProps<Props>();
const authenticityToken = csrfToken();
const formMethod = computed(() => (props.requestMethod === "get" ? "get" : "post"));
const form = ref<HTMLFormElement | null>(null);

// NOTE: 外部からもsubmitしたいケースを考慮して公開しとく
defineExpose({ form });
</script>
<style lang="scss" scoped></style>

実際に利用する際には以下のような感じで利用できます。

<template>
  <common-form request-path="/posts" request-method="post">
    <label for="title" >タイトル</label>
    <input type="text" name="title" />
    <button type="submit">登録する</button>
  </common-form>
</template>
<script lang="ts" setup>
import CommonForm from "@js/components/molecules/CommonForm.vue";
</script>

おわりに

自分で実装してみて改めて思いましたが@rails/ujs便利ですね🙏