Vue.js でフラッシュメッセージ(FlashMessage)

先日、Vue.js での自作ストアを作ったので、これを利用して何か作ろうと考えました。
親子の関係もないコンポーネント間でデータの共有なら「メッセージの表示だな」と感じました。
というわけでフラッシュメッセージを作っていきます。

準備

今回も vite を使って初期設定していきます。

1
2
3
4
5
6
7
8
9
10
11
12
npm init @vitejs/app app --template vue-ts
cd app

# vite での vue.jsの初期インストール
npm install

# 各種ライブラリのインストール
npm install uuid @popperjs/core bootstrap@next
# uuid用の型定義のインストール
npm install --save-dev @types/uuid

npm run dev

実装

それでは、以下具体的な実装です。

編集:src/main.ts

src/main.ts は、bootstrap の読み込みと、後から示す store の登録をします。

src/main.ts
1
2
3
4
5
6
7
8
9
10
11
12
import { createApp } from "vue";
import App from "./App.vue";

// store関連
import store, { storeKey } from "./store/store";

// bootstrap関連
import "bootstrap";
import "bootstrap/dist/css/bootstrap.min.css";

// storeの登録
createApp(App).provide(storeKey, store).mount("#app");

新規:src/store/store.ts

ストアの内容は次の通り。

src/store/store.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import { reactive, computed, ComputedRef, InjectionKey } from "vue";
import { v4 as uuidv4 } from "uuid";

// messageの型定義
interface Message {
uuid: string;
text: string;
timeoutFlug: boolean;
}

// storeのデータ、関数定義
export interface store {
messages: ComputedRef<Message[]>;
addMessage: (text: string, time: number) => void;
deleteMessage: (uuid: string) => void;
}

// キーの追加
export const storeKey: InjectionKey<store> = Symbol("store");

//メッセージデータ本体
const _messages: Message[] = [];
let messages = reactive(_messages);

//メッセージを配列へ登録
const addMessage = (text: string, time: number) => {
const uuid: string = uuidv4();

messages.push({ uuid, text, timeoutFlug: false });

setTimeout(() => {
deleteMessage(uuid);
}, time);
};

// メッセージを配列から削除
const deleteMessage = (uuid: string) => {
let tmpMessage = [...messages];
tmpMessage = tmpMessage.filter((message) => !(message.uuid === uuid));
messages.splice(0, messages.length);
messages.push(...tmpMessage);
};

export default {
messages: computed(() => messages),
addMessage,
deleteMessage,
};

編集:src/App.vue

src/App.vue は、フラッシュメッセージを表示する FlashMessage.vue と AddMessage.vue を読み込んで表示するだけ。

src/App.vue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<template>
<flash-message />
<add-message />
</template>

<script lang="ts">
import { defineComponent } from "vue";
import AddMessage from "./components/AddMessage.vue";
import FlashMessage from "./components/FlashMessage.vue";

export default defineComponent({
name: "App",
components: {
AddMessage,
FlashMessage,
},
});
</script>

<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>

src/components/AddMessage.vue

ストアへのデータ登録する関数を addMessage を呼び出すだけ。
(確認だけなのでフォームは適当に)

src/components/AddMessage.vue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<template>
<div class="container">
<form>
<!--適当なフォーム-->
<button type="button" class="btn btn-primary btn-lg" @click="submit">
Submit
</button>
</form>
</div>
</template>

<script lang="ts">
import { defineComponent, inject } from "vue";
import { store, storeKey } from "../store/store";

export default defineComponent({
setup: () => {
const { addMessage } = inject(storeKey) as store;

const submit = () => {
// 何かしら適当なフォームのチェック

// メッセージ文言と、表示秒数を指定して呼び出し
addMessage("Email と パスワードを正しく入力してください", 3000);
};

return { addMessage, submit };
},
});
</script>

src/components/FlashMessage.vue

フラッシュメッセージを表示する FlashMessage.vue は次のようになります。

src/components/FlashMessage.vue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<template>
<div class="messages container-fluid flash-container">
<div
v-for="message in messages"
:key="message.uuid"
class="alert alert-danger flash-message"
role="alert"
>
{{ message.text }}
</div>
</div>
</template>

<script lang="ts">
import { defineComponent, inject } from "vue";
import { store, storeKey } from "../store/store";

export default defineComponent({
setup() {
const { messages } = inject(storeKey) as store;
return { messages };
},
});
</script>

<style scoped>
.flash-container {
position: absolute;
}

.flash-message {
position: relative;
left: 0;
right: 0;
margin-top: 10px;
z-index: 100;
opacity: 0.9;
}
</style>

動作確認

ここまで作って、npm run devで起動すると次のように動きます。


今回は store を介してデータを共有して、フラッシュメッセージを表示してみました。
DI はだいぶ使いこなせた気がします。

ではでは。