(この記事のサンプルコードは Vue2 です)
Component を関数として呼び出したくなった。
なんで?
こんな感じのことが起こった。
- データの保存ボタンを押したら、特定の条件下でユーザーに本当に保存するか確認したい
- ユーザーは「本当に保存しますか?」的なモーダルに「はい」か「いいえ」で答える
これを普通にやると、
- モーダルの開閉を管理する ref を用意
- それを保存ボタンのハンドラーで更新し、モーダルを表示する
- モーダルが emit したイベントを受け取って「はい」か「いいえ」を得る
- 「はい」だったら保存する
って感じになりそう。
すでにある保存のロジックにあまり手を加えずに確認モーダルをだしたかったので、コンポーネントの呼び出しを関数化することにした。
どうするか
こうする。
import Vue from 'vue';
import Component from './ConfirmHogeModal.vue';
const ComponentConstructor = Vue.extend(Component);
const initComponent = (hoge: string) => new ComponentConstructor({
el: document.createElement('div'),
props: {
hoge: {
type: String,
default: hoge,
},
},
});
export const confirmHoge = (hoge: string): Promise<boolean> => {
let resolve;
const p = new Promise<boolean>(r => {
resolve = r;
});
const instance = initComponent(hoge);
instance.$on('confirm', (ok: boolean) => {
document.body.removeChild(instance.$el);
instance.$destroy();
resolve(ok);
});
document.body.appendChild(instance.$el);
return p;
};
ここでの ConfirmHogeModal.vue
は prop として hoge
を受け取り、confirm
イベントを emit するものである。
それを初期化して自力で body に突っ込み、必要がなくなったらインスタンスを破棄して、body から取り除く。
そのコンポーネントが confirm
イベントを発火したときに resolve するような Promise を返している。
こうすることにより、好きな場所で以下のようにできる。
const saveButtonHandler = async (input) => {
// validate input
// ...
const ok = await confirmHoge(input.hoge)
if (!ok) {
return
}
// call api
// ...
}
このようなモーダルは
- template のどこに置くかは全く重要ではない
- とにかく表示されて結果が得られれば良い
- モーダルを閉じることがイコール結果が決定することである
以上のような性質から関数化しやすく、ユーザーの入力を待つ Promise を作ると便利だった。
以上です。