Nuxt + Vuex + Vuetifyで簡易的なフラッシュメッセージを実装する

BFFでアプリを作る際、railsのフラッシュメッセージのような機能がフロントでも欲しかったので、簡易的に実装しました。

こんなものができます。
![flush.mov.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252494/f5aef94d-27fa-0e27-bdb7-9390d05be0ce.gif)

### 環境

```
Nuxt: 2.8.0
Vuex: 3.1.1
Vuetify: 1.5.14
```

## storeの用意

まずはフラッシュメッセージを格納するためのstoreをVuexに実装していきましょう。
私のディレクトリ構成はこのようになっています。
<img width="281" alt="スクリーンショット 2019-10-22 22.13.39.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252494/a898cb86-0d66-6a09-3cc2-bd2873163567.png">
flush_messageフォルダ配下にactions getters mutations stateを置いています。
が、今回はaction.jsは使用しません。
各ファイルの処理を記載していきます。

```javascript:state.js
export default () => ({
  text: '', //フラッシュメッセージの内容が入る
})
```

```javascript:mutations.js
export default {
  setMessage: (state, payload) => {
    state.text = payload.text; // stateの状態を変更する
  }
}
```

```javascript:actions.js
export default {
  async showFlashMessage({commit}, message) {
    commit('setMessage', message); //mutationに値を渡す
  }
}
```

Vuex側はこれで準備完了。

## アラートのコンポーネント作成
次にこちらを呼び出して変更するためのview側を実装していきます。
まずはアラートのコンポーネントを作成します。

```vue:notification.vue
<template>
  <v-alert
    v-bind="$attrs"
    :class="[`elevation-${elevation}`]"
    :value="value"
    class="v-alert--notification"
>
    <slot />
  </v-alert>
</template>

<script>
export default {
  inheritAttrs: false,
  
  props: {
    elevation: {
      type: [Number, String],
      default: 6
    },
    value: {
      type: Boolean,
      default: true
    }
  }
}
</script>

<style>
.v-alert--notification {
  border-radius: 4px !important;
  border-top: none !important;
}
</style>

```

実際にこのコンポーネントを使用していきます。
import先のパスに注意。

```show-notification.vue
<template>
  <notification
    class="mb-3"
    color="info"
    >
    {{ $store.state.flush_message.text }}
  </notification>  
</template>

<script>
  import notification from '~/components/material/notification'

  export default {
    components: {
      notification
    }
  }
</script>

<style>

</style>

```

ひとまずこちらで空のメッセージボックスが出てきました。
<img width="225" alt="スクリーンショット 2019-10-22 23.00.26.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252494/7338927b-ad17-cd9d-803d-e3d21e2b5023.png">
Vuex側のtextの値を変更する処理をして、この中にメッセージを追加していきましょう。

## メッセージを更新させる
ここでは、

**①別ページでボタンを用意**
**②ボタンを押すとstore内のtextの値を更新**
**③その後、上記のshow-notificationのページにリダイレクトされる**
**④リダイレクト先で更新したメッセージが表示される**
といった風に実装します。

こんな感じです。

```vue:button.vue
<template>
  <v-btn
    class="mx-0 font-weight-light"
    color="success"
    v-on:click="submit"
  >
    送信
  </v-btn>
</template>

<script>
  import { mapActions } from 'vuex'
  
  export default {
    methods: {
      ...mapActions({
        showFlashMessage: 'flush_message/showFlashMessage',
        submit: function() {
        this.showFlashMessage({ text: "投稿完了" }); // ①フラッシュメッセージをセット
        this.$router.push({ path: "/show-notification" }); // ②show-notificationにリダイレクト
      },
      })
    }
  }
</script>
```

①でactions.jsで定義したメソッドを呼び出し、storeの値を更新させます。
その後、②でメッセージボックスを配置したページにリダイレクトさせます。
<img width="207" alt="スクリーンショット 2019-10-23 0.21.31.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252494/e888c4ef-fae2-0f2e-e3dc-48c276f0c633.png">
値がちゃんと渡されました！

## フラッシュさせる
さてメッセージが表示されたは良いものの、このままでは
・値が消えない限りそのまま
・値が消えても枠だけ残る
となってしまいます。
フラッシュメッセージらしく必要に応じて出たり消えたりするようにしてあげましょう。

```vue:show-notification.vue
<template>
  <notification
    class="mb-3"
    color="info"
    v-if="$store.state.flush_message.text" //追加
    >
    {{ $store.state.flush_message.text }}
  </notification>  
</template>

<!-- 以下省略 -->
```

フラッシュメッセージを呼び出している部分に、v-if属性を足してあげましょう。
これでtextに値がセットされているときだけ表示されるようになりました。

### storeの値を空にする
このままでは常に表示されたままになってしまいます。
最後にセットした値を空にする処理を追加しましょう。
nuxtのミドルウェアという機能を使って実装します。

middlewareフォルダを作成し、その直下にclearNotification.jsというファイルを作成します。

```javascript:middleware/clearNotification.js
export default function ({ store, redirect }) {
  store.dispatch('flush_message/showFlashMessage', {text: null});
}
```

こちらでtextの値をnullにしてあげます。
ミドルウェアはページを読み込む前に指定した処理を挟める機能です。
こちらを使って、特定のページにアクセスした際にtextの値がnullになる、といった風に実装します。
実環境で使用するイメージとしては、
<img width="912" alt="スクリーンショット 2019-10-23 0.51.13.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252494/31ca4e94-d017-a809-508c-1cf9ef630bfd.png">
こんな感じです。
上記で作ったものだと、それぞれ
・show→show-notification.vue
・edit,create→button.vue
が担当します。

本当は非同期処理とか使って○秒後に非表示、といった処理ができればよかったのですが、JS初心者にはハードル高くて諦めました...
この内容だと、showのURLをID直接叩いた時に意図しないメッセージが出たりすると思うので、自身の用途に合わせて使ってください。

では上記のindexに値する、textをnullにする処理を行うページを実装します。

```vue:index.vue
<template>
  <p>text= nullにするだけのページ</p>
</template>

<script>  
  export default {
    middleware: 'clearNotification',
  }
</script>
```

このように、export default内で指定するだけで勝手に処理してくれます。
メッセージが出ているのを確認した後こちらのページに飛び、また元のページに戻ると何も表示されないのが確認できると思います。

ボタンを押すところの処理など、axiosに組み込んで使ってみてください！
JS初心者のためかなり力技での実装です。
変な所があったらご指摘お願いします！！！