Madogiwa Blog

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

Vue.js: `@rails/ujs`を利用した`link_to`の`method`属性によるhttp method指定を再現してみたのでメモ📝

Ruby on Railslink_toには、methodを指定してGET以外のリクエストを送信する機能があります。Viewを含めてフロント周りをVue.jsに置き換えるときに、この辺りの再現がネックになったのでVue.jsでの再現方法をメモ📝

You can bind to the same Ajax events as form_with. Here's an example. Let's assume that we have a list of articles that can be deleted with just one click. We would generate some HTML like this:

 <%= link_to "Delete article", @article, remote: true, method: :delete %>

Working with JavaScript in Rails — Ruby on Rails Guides

前提事項

以下の記事で記載したFormコンポーネントが存在することが前提です。

madogiwa0124.hatenablog.com

<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;
};
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);
defineExpose({ form });
</script>
<style lang="scss" scoped></style>

実装したコード

以下のようなコンポーネントを用意して再現してみました。仕組みはget以外の場合には前提に記載したCommonFormを非表示要素として入れて、 aタグクリックのeventをpreventし、Formをsubmitイベントを呼び出すようにしています。

<template>
  <a v-if="method === 'get'" class="common-link" :href="href">
    <slot />
  </a>
  <a v-else class="common-link" @click.prevent="handleOnSubmit">
    <slot />
    <CommonForm ref="linkForm" class="common-link__link-form" :request-method="method" :request-path="href" />
  </a>
</template>
<script lang="ts" setup>
import type { HttpRequestMethod } from "@js/types/types";
import CommonForm from "@js/components/molecules/CommonForm.vue";
import { ref } from "vue";

type PropsType = { href: string; method?: HttpRequestMethod };
withDefaults(defineProps<PropsType>(), { method: "get" });
const linkForm = ref<InstanceType<typeof CommonForm> | null>(null);
const handleOnSubmit = (_e: Event) => {
  // NOTE: CommonFormのformをsubmitする
  linkForm.value?.form?.submit();
};
</script>
<style lang="scss" scoped>
@use "@css/variables" as *;

.common-link {
  &__link-form {
    display: none;
  }
}
</style>

以下のような感じで利用することができます。

<CommonLink :href="/sessions" method="delete">ログアウト</CommonLink>