ymmooot

Vue Component を関数として呼び出す

(この記事のサンプルコードは Vue2 です)

Component を関数として呼び出したくなった。

なんで?

こんな感じのことが起こった。

  1. データの保存ボタンを押したら、特定の条件下でユーザーに本当に保存するか確認したい
  2. ユーザーは「本当に保存しますか?」的なモーダルに「はい」か「いいえ」で答える

これを普通にやると、

  • モーダルの開閉を管理する 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 を作ると便利だった。

 

以上です。