カテゴリー
コンピューター

Vue.js 、例えば検索を実行して同ページに結果を表示する時に、検索条件を URL クエリ引数に反映させたい。その方法

こんなケースを想定してみる

ページの検索フォームに入力して検索ボタンをクリックすると AJAX で検索用 API を実行する。この時に、検索パラメータを URL のクエリパラメータとして URL へ反映することはできるのか?

また、ブラウザでの戻る、進むをクリックした時も、検索結果をページに表示するのはもちろん、 URL クエリ引数や検索フォームに検索キーワードを反映させたい。

実装方法まとめ

以下、 Vue.js 2 (Options API) での勘所です。 Compotision API でもポイントは同じで、メソッドを適宜置き換えてください。どちらの実装例も本記事にあるので、具体例を参考にするのも、良いと思います。

  • ページ初回表示時は created メソッド内でリソースサーバからデータ取得を行う。
  • 検索ボタンをクリックしたら自分のページへ URL クエリ引数つきで Vue Router の push を行う。例えば this.$router.push({ name: "AboutA, query: { base: "JPY" } }) となる。これにより beforeRouteUpdate メソッドが実行され、ここでリソースサーバからデータ取得を行う。
  • リソースサーバからデータ取得を行う処理 (メソッド) では次を気をつけると良い。
    • 引数として、クエリパラメータを受け取る。
    • メインの処理 (リソースサーバからのデータ取得) 以外に、検索フォームへ対応する URL クエリ引数の値を設定する、などの処理を行う。

読むべき公式ページ

これらの Vue Router の公式ページをしっかりと読み込んで理解できれば、本ページでやりたいことは実現できました。

コード例

Options API

<template>
  <div class="about">
    <h1>This is an about page</h1>
    <input v-model="paramUserId" />
    <button type="button" @click="pushRouter">get</button>
    <p>{{ exchangeRates }}</p>
  </div>
</template>

<script>
export default {
  name: "About",
  async beforeRouteUpdate(to, from, next) {
    // ブラウザバック時、 router.push 時にここに来る
    await this.getData(to.query);
    next();
  },
  data: () => ({
    exchangeRates: {},
    paramUserId: "",
  }),
  created() {
    this.getData(this.$route.query);
  },
  methods: {
    async getData(query) {
      // 検索フォーム等へ URL クエリ引数を設定
      this.paramUserId = query?.userId ?? "";

      const baseUrl = "https://jsonplaceholder.typicode.com/posts";
      const params = new URLSearchParams(query);
      const url = `${baseUrl}?${params}`;
      console.log(url);
      const response = await fetch(url).then((resp) => resp.json());
      this.exchangeRates = { ...response };
    },
    pushRouter() {
      const query = {};
      if (this.paramUserId) {
        query.userId = this.paramUserId;
      }
      this.$router.push({ name: "About", query });
    },
  },
};
</script>

Composition API

<template>
  <div class="about">
    <h1>This is an about page</h1>
    <input v-model="paramUserId" />
    <button type="button" @click="pushRouter">get</button>
    <p>{{ exchangeRates }}</p>
  </div>
</template>

<script>
import { defineComponent, onMounted, ref } from "vue";
import { onBeforeRouteUpdate, useRouter, useRoute } from "vue-router";

export default defineComponent({
  name: "About",
  setup() {
    const router = useRouter();
    const route = useRoute();
    const exchangeRates = ref([]);
    const paramUserId = ref("");

    onBeforeRouteUpdate((to) => {
      // ブラウザバック時、 router.push 時にここに来る
      getData(to.query);
    });

    const getData = async (query) => {
      // 検索フォーム等へ URL クエリ引数を設定
      paramUserId.value = query?.userId ?? "";

      const baseUrl = "https://jsonplaceholder.typicode.com/posts";
      const params = new URLSearchParams(query);
      const url = `${baseUrl}?${params}`;
      const response = await fetch(url).then((resp) => resp.json());
      exchangeRates.value = response;
    };
    onMounted(getData(route.query));

    const pushRouter = () => {
      const query = {};
      if (paramUserId.value !== "") {
        query.userId = paramUserId.value;
      }
      router.push({ name: "About", query });
    };

    return {
      exchangeRates,
      paramUserId,
      pushRouter,
    };
  },
});
</script>

おわりに

実際に使ったリソースサーバ、 API は次です。

Exchange Rates API は開発中にサービスの形態が変わったのか、試行回数の上限に達したのか、途中から登録が必要となってしまいました。 それで Vue.js 3 options API 版を今回書きました。けれども、本当は Vue.js 3 Compotision API 版も書きたかったのですが、できませんでした。また別の登録不要で検索にも対応した良さそうな API があれば、試してみようと思います。

後日追記。制限がなさそうな API へと変更しました。

以上です。

コメントを残す