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?

More than 1 year has passed since last update.

【Vue.js, Nuxt.js】storeを使用してTypeScript側からsnackbarを起動させる

Last updated at Posted at 2022-05-28

この記事について

以下のようなsnackbarをVueのtemplate上での v-ifv-show での表示制御ではなく、TypeScript側から任意のタイミングで表示できるようにする方法をこの記事で紹介します。
image.png

対象読者

  • Vue.js(やNuxt.js)でsnackbarをTypeScript側から表示したい方
  • Vue.jsのstoreの実装を行ったことのある方
  • Vuetifyの(簡易的な)実装を行ったことのある方

環境

内容 バージョン
nuxt 2.15.8
nuxt-typed-vuex 0.3.0
vue 2.6.14
vuetify 2.6.1

以下の内容は、Nuxt.jsを使用していることを前提とします。
ディレクトリ構成

sampleProject
├componens
| └snackbarError.vue
├pages
| └index.vue
├store
| ├index.ts
| └snackbarError.ts
└types
  └index.d.ts

処理の流れ

考え方としては、「スナックバー用のstoreを作成して、storeの値変更時にスナックバーコンポーネントを表示する」として、「スナックバーコンポーネントをグローバルに扱う」考え方としております。

スナックバーを表示したい処理の実行

スナックバーstoreのメッセージデータ更新

スナックバーstoreのメッセージデータ更新をトリガにスナックバーコンポーネントを表示

スナックバー用のstoreの作成

storeはスナックバー用の個別storeを管理する snackbarError.ts と 他のstoreも含めて管理する index.ts に分けております。

store/snackbarError.ts
import { mutationTree, actionTree } from 'typed-vuex';

// スナックバーコンポーネント上に表示するメッセージのデータをstoreに保持する
export const state = () => ({
  message: '' as string,
});
export type RootState = ReturnType<typeof state>;

export const mutations = mutationTree(state, {
  open(state, message: string) {
    state.message = message;
  },
});

export const actions = actionTree(
  { state, mutations },
  {
    open({ commit }, message: string) {
      commit('open', message);
    },
  },
);
store/index.ts
import {
  getAccessorType,
  getterTree,
  mutationTree,
  actionTree,
} from 'typed-vuex';
// その他のstoreがある場合はここで他のstoreをimportする
import * as snackbarError from './snackbarError';

export const state = () => ({});
export const getters = getterTree(state, {});
export const mutations = mutationTree(state, {});
export const actions = actionTree({ state, getters, mutations }, {});
export const accessorType = getAccessorType({
  state,
  getters,
  mutations,
  actions,
  modules: {
    // その他のstoreがある場合はmodulesに他のstoreを追加する
    snackbarError,
  },
});

VueファイルのTypeScriptからstoreにアクセスできるように、index.d.ts を以下のように編集します。

types/index.d.ts
import { accessorType } from '~~/store';

declare module 'vue/types/vue' {
  interface Vue {
    $accessor: typeof accessorType;
  }
}

declare module '@nuxt/types' {
  interface NuxtAppOptions {
    $accessor: typeof accessorType;
  }
}

スナックバーコンポーネントの作成

先ほど作成したスナックバーstoreのstate変更を検知するように実装( this.$store.subscribe の部分)します。
スナックバーはVuetifyのコンポーネントを使用しております。

componens/snackbarError.vue
<template>
  <v-snackbar v-model="isShown" top max-width="60%" color="error">
    <div class="pre-wrap" v-text="message" />

    <template #action="{ attrs }">
      <v-btn
        small
        plain
        shaped
        multi-line
        v-bind="attrs"
        @click="onClickedCloseButton"
      >
        <v-icon> mdi-close </v-icon>
      </v-btn>
    </template>
  </v-snackbar>
</template>

<script lang="ts">
import Vue from 'vue';

export default Vue.extend({
  name: 'SnackbarError',
  data() {
    return {
      isShown: false,
      message: '',
    };
  },
  created() {
    // storeの値変更を検知して、storeに送られたメッセージを画面上に表示する
    this.$store.subscribe((mutation, state) => {
      if (mutation.type === 'snackbarError/open') {
        const message = state.snackbarError.message;
        if (message === null || message === '') {
          return;
        }

        this.message = message;
        this.isShown = true;
      }
    });
  },
  methods: {
    onClickedCloseButton(): void {
      this.isShown = false;
    },
  },
});
</script>

<style lang="sass" scoped>
// 改行コードはそのまま改行して表示
.pre-wrap
  white-space: pre-wrap
</style>

TypeScriptからスナックバーを表示

スナックバーを表示する際は、components/snackbarError.vue を直接呼び出すのではなく、先ほど作成したstoreにメッセージを送ってstore経由でスナックバーを表示します。
vueのtemplate内でsnackbarを v-ifv-show で表示制御せずともTypeScriptから表示することができるようになりました。

pages/index.vue
<template>
  <button @click="showError">エラー表示</button>
</template>

<script lang="ts">
import Vue from 'vue';

export default Vue.extend({
  methods: {
    showError() {
      this.$accessor.snackbarError.open('エラーが発生しました。');
    },
  },
});
</script>

Nuxt.jsの場合は、pluginsやmiddlewareなどTypeScriptの Context 型にアクセスできる箇所であれば以下のように記述することでsnackbarを起動することができるようになります。

types/index.d.ts
import { accessorType } from '@f/store';

declare module 'vue/types/vue' {
  interface Vue {
    $accessor: typeof accessorType;
  }
}

declare module '@nuxt/types' {
  interface NuxtAppOptions {
    $accessor: typeof accessorType;
  }
  // Context型からもstoreへアクセスできるように型定義を追加する
  interface Context {
    $accessor: typeof accessorType;
  }
}
import { Context, Plugin } from '@nuxt/types';

const plugin: Plugin = ({ $accessor }: Context) => {
  $accessor.snackbarError.open('エラーが発生しました。');
};

export default plugin;

参考

How to create a global snackbar using Nuxt, Vuetify and Vuex.

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?