0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

[vue3-jp-admin]Vue3 + Vuetify3でv-alertを関数的に操作する方法とPiniaでの管理

Posted at

Vue3 + Vuetify3でのv-alert関数式呼び出しの実装

【vue3-jp-admin】は長期的なプロジェクトであり、継続的に新しい技術を導入していきます。問題がある場合や開発に参加したい場合は、メール(yangrongwei1996@gmail.com)でご連絡ください。また、GitHubのissuesを通じて質問や提案をお寄せいただくことも可能です(https://github.com/yangrongwe/vue3-jp-admin/issues)。

皆さん、こんにちは!【vue3-jp-admin】にご興味を持っていただきありがとうございます。皆さんの「いいね」やコメントが、私の継続的な更新の励みになっています。:grin:

今回の実装内容は、Vue3でのv-alertの関数呼び出し機能です。デモはこちらからご覧いただけます。:robot:

機能概要

  • 集中状態管理: PiniaやVuexを利用して、v-alertの追加や削除を関数経由で管理
  • 複数アラートの表示: 複数のアラートが順番に表示され、タイムアウトで自動的に消えます
  • alert.png

実装手順

以下に、Vue3 と Vuetify3 を使った v-alert の関数式呼び出し機能の実装手順をまとめます。

1. v-alertコンポーネントの封装

以下のコードで v-alert コンポーネントを封装します。

<template>
  <div>
    <div v-for="(alert, index) in alerts" :key="alert.id">
      <v-alert
        v-bind="alert.props"
        :class="alertClasses"
        :style="getTopStyle(index)"
        @click="removeAlert(alert.id)"
      >
        {{ alert.message }}
      </v-alert>
    </div>
  </div>
</template>

<script lang="ts">
import { defineComponent, computed } from 'vue';
import { VAlert } from 'vuetify/components';
import { useDisplay } from 'vuetify';
import { useAlertStore } from '@/store/alert';

export default defineComponent({
  name: 'AlertComponent',
  components: {
    VAlert,
  },
  setup() {
    const alertStore = useAlertStore();
    const alerts = computed(() => alertStore.alerts);
    const { mobile } = useDisplay();

    const alertClasses = computed(() =>
      mobile.value
        ? ['tw-absolute', 'tw-z-[9999]', 'tw-inset-x-[15px]', 'tw-w-auto']
        : ['tw-absolute', 'tw-z-[9999]', 'tw-inset-x-[20%]', 'tw-w-auto']
    );

    const getTopStyle = (index: number) => {
      const topPosition = 150 + index * 80;
      return { top: `${topPosition}px` };
    };

    const removeAlert = (id: number) => {
      alertStore.removeAlert(id);
    };

    return {
      alertClasses,
      alerts,
      getTopStyle,
      removeAlert,
    };
  },
});
</script>

<style scoped lang="scss"></style>

2. Pinia の封装

Pinia ストアの実装は以下の通りです。これにより、アラートの状態管理を簡単に行えます。

import { defineStore } from 'pinia';

interface Alert {
  id: number;
  message: string;
  timeout: number;
  props: Record<string, any>;
}

export const useAlertStore = defineStore('alert', {
  state: () => ({
    alerts: [] as Alert[], // アラートのリスト
  }),
  actions: {
    // アラートを追加する
    addAlert(alert: Omit<Alert, 'id'>) {
      const id = Date.now();
      const newAlert = { ...alert, id };
      this.alerts.push(newAlert);
      if (alert.timeout) {
        setTimeout(() => {
          this.removeAlert(id);
        }, alert.timeout);
      }
    },
    // 特定の ID のアラートを削除する
    removeAlert(id: number) {
      this.alerts = this.alerts.filter((alert) => alert.id !== id);
      if (this.alerts.length === 0) {
        this.$reset();
      }
    },
    // すべてのアラートをクリアする
    clearAlerts() {
      this.alerts = [];
    },
  },
});

3. 呼び出しツールの封装

アラートを表示するためのツールを以下のコードで封装します。

import { createApp } from 'vue';
import AlertComponent from '@/components/JpAlert/index.vue';
import { useAlertStore } from '@/store/alert';
import vuetify from '@/plugins/vuetify/index.ts';

interface AlertOptions {
  message: string;
  timeout?: number;
  props?: Record<string, any>; // v-alert の props
}

// グローバル変数の作成
let appInstance: ReturnType<typeof createApp> | null = null;
let mountPoint: HTMLElement | null = null;

export function showAlert(options: AlertOptions): void {
  const { message, timeout, props = {} } = options;

  if (!appInstance) {
    // アプリケーションインスタンスの作成
    appInstance = createApp(AlertComponent);
    appInstance.use(vuetify);

    // マウントポイントの作成とマウント
    mountPoint = document.createElement('div');
    document.body.appendChild(mountPoint);
    appInstance.mount(mountPoint);
  }

  // Pinia で状態管理
  const alertStore = useAlertStore();
  alertStore.addAlert({ message, timeout, props });
}

export function clearAllAlerts(): void {
  // Pinia で管理されている全てのアラートをクリア
  const alertStore = useAlertStore();
  alertStore.clearAlerts();

  // アプリケーションインスタンスとマウントポイントをアンマウントし、削除
  if (appInstance && mountPoint) {
    appInstance.unmount();
    document.body.removeChild(mountPoint);
    appInstance = null;
    mountPoint = null;
  }
}

4. Hook の使用例

フックを使用してアラートを表示する方法は以下の通りです。

import { showAlert, clearAllAlerts } from '@/utils/dynamicAlert';
import { MainTitle } from '@/types/index';

const mainTitle: MainTitle = {
  title: 'Vuetify3 アラート 二次封装【関数式呼び出し】',
  linkText: 'src/views/componentsDemo/alert/index.vue',
  path: 'https://github.com/yangrongwe/vue3-jp-admin/blob/main/src/views/componentsDemo/alert/index.vue',
};

const handleShowAlert = (btnIndex: number) => {
  switch (btnIndex) {
    case 1:
      showAlert({
        message: 'This is an alert default!',
        props: {
          closable: true,
        },
      });
      break;
    case 2:
      showAlert({
        message: 'This is an alert outlined!',
        props: {
          variant: 'outlined',
          closable: true,
        },
      });
      break;
    case 3:
      showAlert({
        message: 'This is an alert tonal!',
        props: {
          variant: 'tonal',
          closable: true,
        },
      });
      break;
    case 4:
      showAlert({
        message: 'This is an alert info!',
        props: {
          type: 'info',
          closable: true,
          border: 'start',
          borderColor: 'white',
        },
      });
      break;
    case 5:
      showAlert({
        message: 'This is an alert success!',
        props: {
          type: 'success ',
          closable: true,
          border: 'top',
          borderColor: 'white',
        },
      });
      break;
    case 6:
      showAlert({
        message: 'This is an alert warning!',
        props: {
          type: 'warning ',
          closable: true,
          border: 'bottom',
          borderColor: 'white',
        },
      });
      break;
    case 7:
      showAlert({
        message: 'This is an alert error!',
        props: {
          type: 'error',
          closable: true,
          border: 'end',
          borderColor: 'white',
        },
      });
      break;
    case 8:
      showAlert({
        message: 'This is an alert timeout!',
        timeout: 3000,
        props: {
          type: 'info',
          closable: true,
          border: 'start',
          borderColor: 'white',
        },
      });
      break;
    default:
      break;
  }
};

const handleClearAllAlerts = () => {
  clearAllAlerts();
};

export { handleShowAlert, handleClearAllAlerts, mainTitle };

最後に関数式でのコンポーネントの封装はそれほど難しくありませんが、メモリリークに注意が必要です。例えば、clearAllAlerts で Vue インスタンスを削除するのを忘れると、メモリに不要なデータが残る可能性があります。今回の実装が皆さんのプロジェクトに役立つことを願っています!

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?