- はじめに
- 出来上がりはこんな感じ!
- モーダルダイアログの出し方(考え方)
- 開発環境
- プロジェクトの作成
- コンポーネントの構成
- 親コンポーネント(ModalDialogSample.vue)
- 子コンポーネント(ModalDialog.vue)
- 出来上がり!
- 謝辞
はじめに
UIを構築において必要不可欠な要素とも言ってよいモーダルダイアログをVueとTypeScriptで作ってみましたので記事にまとめておきます。作成に当たってはこの公式サイトの例がシンプルでとても参考になったので、これをさらにシンプルにして要点を絞って解説してみます。
出来上がりはこんな感じ!
いつもと同じようにまずは、出来上がりのイメージです。
ボタンクリックでモーダルダイアログを表示、OKで入力テキストを返しダイアログを閉じてテキストを表示、Cancelで何もせずダイアログを閉じる。といった振る舞いのサンプルに挑戦です。
コードはここです。
GitHub - Shikataramuno/modal-dialog-sample at sample
モーダルダイアログの出し方(考え方)
公式サイトを見て、私なりに理解した内容です。
モーダルダイアログを子コンポーネントとして定義しておき、親コンポーネント側でv-if
ディレクティブを使って子コンポーネントの表示を制御するというアプローチです。
子コンポーネント(モーダルダイアログコンポーネント側)のスタイル指定でz-index
を大きな値にして画面全体にマスクをかけ、その上にダイアログの要素を配置するようにします。こうすることで、子コンポーネントを表示させたときに親コンポーネントがマスクされ、子コンポーネントがモーダルダイアログとして表示されるようになります。ダイアログを閉じるときには子コンポーネントから親コンポーネントへイベントをemit
し、v-if
ディレクティブの条件を非表示に切り替える様にします。
では、簡単なサンプルを作りながら確認していきしょう。
開発環境
使用したツールは以下のとおり。(他の記事と同様です。)
> node --version v8.9.4 > npm --version 5.6.0 > vue --version 3.1.1
プロジェクトの作成
以下のコマンドでプロジェクトを作成します。
vue create moda-dialog-sample
パラメタの指定はこの記事を参照してください。
コンポーネントの構成
作成したプロジェクトのsrc/components
フォルダにダイアログを呼び出す側のコンポーネントとダイアログ本体のコンポーネントをそれぞれ定義します。
src │ App.vue │ main.ts │ shims-tsx.d.ts │ shims-vue.d.ts │ ├─assets │ logo.png │ ├─components │ ModalDialog.vue │ ModalDialogSample.vue │ └─style modal.css
ModalDialogSample.vueがダイアログを呼び出す側(i.e. 親)コンポーネント、ModaiDialog.vueがダイアログ本体(i.e. 子)コンポーネントとなります。
親コンポーネント(ModalDialogSample.vue)
HTML
まずはHTMLの構造からですが、とてもシンプルです。
<template> <div class="ModalDialogSampleBase"> <h1>{{ msg }}</h1> <button @click="show"'>ダイアログの表示</button> <div class="message-field">ダイアログ入力メッセージ<br>{{message}}</div> <ModalDialog v-if="showDialog" @ok='ok' @cancel='cancel'/> </div> </template>
6行目で子コンポーネントを配置しています。ここでは v-if ディレクティブを使いshowDialog
の真偽で表示/非表示を切り替えています。
ダイアログの表示
このshowDialog
は4行目のボタンのClickイベントハンドラ内でfalse→trueにセットしダイアログを表示させます。ダイアログの非表示
子コンポーネントからEmitされるイベントを補足し、showDialog
をtrue→falseにリセットしダイアログを非表示にします。 6行目のv-if
の後にある@ok
と@cancel
が子コンポーネントからのイベントを補足してイベントハンドラを呼び出す定義になります。
次にScriptです。
Script
<script lang="ts"> import { Component, Vue } from 'vue-property-decorator'; import ModalDialog from './ModalDialog.vue'; @Component({ components: { ModalDialog, }, }) export default class ModalDialogSample extends Vue { name: string = 'ModalDialogSample'; msg: string = 'モーダルダイアログのサンプル'; message: string = ''; showDialog: boolean = false; show(): void { this.showDialog = true; } ok(message: string): void { this.showDialog = false; this.message = message; } cancel(): void { this.showDialog = false; } } </script>
三つのイベントハンドラを定義しています。
ダイアログ表示ボタンのイベントハンドラが show()
、子コンポーネントからのイベントハンドラが ok()
、cancel()
になります。
それぞれメンバ変数のshowDialog
の真偽をセット、リセットして表示/非表示を切り替えています。
ここで ok(message: string)
は引数に string 型の message
が定義されていますが、これは ok イベントのパラメタでダイアログの input フィールドに入力された文字列を子コンポーネントから受けとる為です。
では、次に子コンポーネント側を見てみましょう。
子コンポーネント(ModalDialog.vue)
子コンポーネントのModalDialog.vueの実装です。
HTML
同じようにまずは、HTMLの構造からです。
<template> <transition name="modal"> <div class="modal-mask"> <div class="modal-wrapper"> <div class="modal-container"> <div class="modal-header">モーダルダイアログ </div> <div class="modal-body"> <input type='text' v-model='message' placeholder="メッセージ入力欄です"> </div> <div class="modal-footer"> <button class="modal-default-button" @click='ok(message)'>OK</button> <button class="modal-default-button" @click='cancel()'>Cancel</button> </div> </div> </div> </div> </transition> </template>
modal-mask
が親コンポーネントを覆う為の要素になります。Styleで背景色を透過させてダイアログを表示した際でも親コンポーネントが薄く見えるように、それっぽくしています。
ダイアログ本体は modal-container
要素の中身がそれにあたります。ヘッダ、ボディ、フッタを定義していてボディにinput要素を、フッタにok、cancelのボタンを配置しています。
この辺りはbootstrap*1のCSSが参考になります。
12,13行目でok、cancelのイベントハンドラを登録しています。ok ハンドラでは引数にmessage
を指定していますが、この message
は 9行目で input にバインドされているので、入力テキストが入ります。ここがイベントハンドラ経由で子→親へ入力テキストを伝える動作の起点になります。
Script
最後にダイアログ本体のScriptです。
<script lang="ts"> import { Component, Emit, Vue } from 'vue-property-decorator'; export default class ModalDialog extends Vue { message: string = ''; @Emit('ok') ok(str: string): void { // } @Emit('cancel') cancel(): void { // } } </script>
これも至ってシンプルです。 ok、cancelのイベントハンドラを定義しているだけです。TypeScript なので従来の this.$emit にあたる実装がデコーレータを使った記述の仕方になっています。 例えば6~9行目の ok イベントハンドラは 従来の JavaScript で書くと
ok(str) { this.$emit('ok', str) }
という感じだと思うのですが、デコーレータを使った記述では、@Emitでemitするイベントを定義し、その後にハンドラを実装するという書き方になります。 また、親コンポーネントに渡すパラメタの指定が暗黙的になってしまっている気がして個人的には違和感を感じて馴染めていません。この辺りご意見あればぜひ、お願いしたいと思います。
出来上がり!
npm run serve
で動かします。
TypeScript 故の戸惑いもありましたが、ひとまずモーダルダイアログの実装に加えコンポーネント間のデータ受け渡しも完成しました。必ずといっていいほど使う局面が多いモーダルダイアログの実装、自分が見るレシピとしても活用したいと思います。
謝辞
最後まで読んでいただきありがとうございます。
今回はモーダルダイアログの実装を具体的に解説してみました。少しでもお役に立てれば幸いです。
*1:npm install bootstrap --saveでインストールできます