createTemplatePromise

createTemplatePromise

Promiseとしてのテンプレート。カスタムダイアログ、モーダル、トーストなどを構築するのに便利です。

使用方法

<script setup lang="ts">
import { createTemplatePromise } from '@vueuse/core'

const TemplatePromise = createTemplatePromise<ReturnType>()

async function open() {
  const result = await TemplatePromise.start()
  // ボタンがクリックされ、結果は 'ok'
}
</script>

<template>
  <TemplatePromise v-slot="{ promise, resolve, reject, args }">
    <!-- あなたのUI -->
    <button @click="resolve('ok')">
      OK
    </button>
  </TemplatePromise>
</template>

特徴

  • プログラム的 - UIをPromiseとして呼び出す
  • テンプレート - 新しいDSLではなく、Vueテンプレートを使用してレンダリング
  • TypeScript - ジェネリック型による完全な型安全性
  • レンダーレス - UIを完全に制御
  • トランジション - Vueトランジションをサポート

この関数は vue-template-promise から移行されました。

使用方法

createTemplatePromiseVueコンポーネント を返し、<script> でテンプレート内で直接使用できます。

import { createTemplatePromise } from '@vueuse/core'

const TemplatePromise = createTemplatePromise()
const MyPromise = createTemplatePromise<boolean>() // ジェネリック型を使用

テンプレート内では、v-slot を使用してPromiseと解決関数にアクセスします。

<template>
  <TemplatePromise v-slot="{ promise, resolve, reject, args }">
    <!-- 何でも可能 -->
    <button @click="resolve('ok')">
      OK
    </button>
  </TemplatePromise>
  <MyPromise v-slot="{ promise, resolve, reject, args }">
    <!-- 別のもの -->
  </MyPromise>
</template>

スロットは最初はレンダリングされません(v-if="false"と同様)、コンポーネントから start メソッドを呼び出すまでです。

// @include: main
// ---cut---
const result = await TemplatePromise.start()

テンプレート内で resolve または reject が呼び出されると、Promiseは解決または拒否され、渡された値が返されます。解決されると、スロットは自動的に削除されます。

引数の渡し方

引数を start に渡すことができます。

import { createTemplatePromise } from '@vueuse/core'

const TemplatePromise = createTemplatePromise<boolean, [string, number]>()
// @include: passing-arguments
// ---cut---
const result = await TemplatePromise.start('hello', 123)

テンプレートスロット内では、args プロパティを介して引数にアクセスできます。

<template>
  <TemplatePromise v-slot="{ args, resolve }">
    <div>{{ args[0] }}</div>
    <!-- hello -->
    <div>{{ args[1] }}</div>
    <!-- 123 -->
    <button @click="resolve(true)">
      OK
    </button>
  </TemplatePromise>
</template>

トランジション

スロットをアニメーション化するためにトランジションを使用できます。

<script setup lang="ts">
const TemplatePromise = createTemplatePromise<ReturnType>({
  transition: {
    name: 'fade',
    appear: true,
  },
})
</script>

<template>
  <TemplatePromise v-slot="{ resolve }">
    <!-- あなたのUI -->
    <button @click="resolve('ok')">
      OK
    </button>
  </TemplatePromise>
</template>

<style scoped>
.fade-enter-active,
.fade-leave-active {
  transition: opacity 0.5s;
}
.fade-enter,
.fade-leave-to {
  opacity: 0;
}
</style>

Vue Transition についてもっと学びましょう。

動機

ダイアログやモーダルをプログラム的に呼び出す一般的な方法は次のようになります:

const dialog = useDialog()
const result = await dialog.open({
  title: 'Hello',
  content: 'World',
})

これはこれらの情報をトップレベルのコンポーネントに送信し、ダイアログをレンダリングさせることで機能します。しかし、UIで表現できる柔軟性が制限されます。例えば、タイトルを赤くしたり、追加のボタンを持たせたりしたい場合があります。このような場合、多くのオプションを持つことになります:

const result = await dialog.open({
  title: 'Hello',
  titleClass: 'text-red',
  content: 'World',
  contentClass: 'text-blue text-sm',
  buttons: [
    { text: 'OK', class: 'bg-red', onClick: () => {} },
    { text: 'Cancel', class: 'bg-blue', onClick: () => {} },
  ],
  // ...
})

これでも十分に柔軟ではありません。もっと多くを望むなら、手動でレンダーファンクションを使うことになるかもしれません。

const result = await dialog.open({
  title: 'Hello',
  contentSlot: () => h(MyComponent, { content }),
})

これはスクリプト内でUIテンプレートを表現するための新しいDSLを再発明するようなものです。

この関数は、スクリプトではなくテンプレートでUIを表現できるようにしつつ、プログラム的に操作できるようにします。