動態 modal v2.0.0+

Vue Final Modal 提供一個輔助函式用於動態顯示 modal,這意味著你不需要將元件 vue-final-modal 新增到你的 Vue 模板 (template) 中,你也不需要使用 v-model 來隱藏或顯示 modal。你可以簡單地執行 $vfm.show 並帶入一個 modal 元件像是下面這個範例:

import { $vfm } from 'vue-final-modal'

$vfm.show({ component: 'MyDynamicModal' })

MyDynamicModal 元件是個舉例,可以到下方查看完整的範例

先決條件

首先,使用動態 modal 的前提是你必須將元件 <ModalsContainer /> 加入你的根元件 App.vue 像是這樣:

App.vue
<template>
  <div>
    ...
    <modals-container></modals-container>
  </div>
</template>
App.vue
<script>
import { ModalsContainer } from 'vue-final-modal'

export default {
  components: {
    ModalsContainer
  }
}
</script>

ModalsContainer 是一個不可見的元件,負責在 Vue 實例中託管動態 modal。你不需要新增任何屬性 (property) 或 HTML 在 ModalsContainer 中,只需要將其引入在你的 Vue 元件樹中,就可以使用動態 modal。

API

$vfm.show(dynamicModalOptions, params)

  • 型別: Function,
  • 參數:
    • dynamicModalOptions: Object
      type dynamicModalOptions = {
        component?: string | Component | AsyncComponent // modal component
        bind?: { [key: string]: any}, // bind props and attrs to modal
        on?: { [key: string]: Function | Function[] } // register events to modal
        slots?: {
          [key: string]: // slot name
            | {
                component: string | Component | AsyncComponent // slot component
                bind?: { [key: string]: any } // bind props and attrs to slot component
                on?: { [key: string]: Function | Function[] } // register events to slot component
              }
            | string
        }
      }
      
    • params: 與 API $vfm.show 相同
  • 回傳: Promise<Object> | Promise<Array>

你可以透過 $vfm.show 這個方法開啟動態 modal。

$vfm.dynamicModals

  • 回傳:
    • Array: 回傳儲存動態 modal 實例的陣列。
  • 範例:
    • 取得第一個創建的動態 modal 實例
        this.$vfm.dynamicModals[0]
      
    • 取得現在創建的動態 modal 總數
        this.$vfm.dynamicModals.length
      

範例

基本

<template>
  <v-button @click="dynamic">Open Dynamic Modal</v-button>
</template>
<script>
export default {
  methods: {
    dynamic() {
      this.$vfm.show({
        component: 'VDynamicModal'
      })
    }
  }
}
</script>

App.vue 中註冊 modals-container 元件。

App.vue
<template>
  <div>
    ...
    <modals-container></modals-container>
  </div>
</template>
App.vue
<script>
import { ModalsContainer } from 'vue-final-modal'

export default {
  components: {
    ModalsContainer
  }
}
</script>

VDynamicModal.vue

<template>
  <vue-final-modal
    v-slot="{ close }"
    v-bind="$attrs"
    classes="modal-container"
    content-class="modal-content"
    v-on="$listeners"
  >
    <button class="modal__close" @click="close">
      <mdi-close></mdi-close>
    </button>
    <span class="modal__title">Hello, vue-final-modal</span>
    <div class="modal__content">
      <p>Vue Final Modal is a renderless, stackable, detachable and lightweight modal component.</p>
    </div>
  </vue-final-modal>
</template>
<script>
export default {
  inheritAttrs: false
}
</script>
<style scoped>
::v-deep .modal-container {
  display: flex;
  justify-content: center;
  align-items: center;
}
::v-deep .modal-content {
  position: relative;
  display: flex;
  flex-direction: column;
  margin: 0 1rem;
  padding: 1rem;
  border: 1px solid #e2e8f0;
  border-radius: 0.25rem;
  background: #fff;
}
.modal__title {
  margin: 0 2rem 0 0;
  font-size: 1.5rem;
  font-weight: 700;
}
.modal__close {
  position: absolute;
  top: 0.5rem;
  right: 0.5rem;
}
</style>

<style scoped>
.dark-mode div::v-deep .modal-content {
  border-color: #2d3748;
  background-color: #1a202c;
}
</style>

進階

<template>
  <v-button @click="dynamic">Open Dynamic Modal</v-button>
</template>
<script>
import VContent from '../VContent.vue'

export default {
  methods: {
    dynamic() {
      this.$vfm.show({
        component: 'CustomModal',
        bind: {
          name: 'VDynamicAdvacedModal'
        },
        on: {
          // event by custom-modal
          confirm(close) {
            console.log('confirm')
            close()
          },
          cancel(close) {
            console.log('cancel')
            close()
          },
          // event by vue-final-modal
          'click-outside'() {
            console.log('@click-outside')
          },
          'before-open'() {
            console.log('@before-open')
          },
          opened() {
            console.log('@opened')
          },
          'before-close'() {
            console.log('@before-close')
          },
          closed() {
            console.log('@closed')
          }
        },
        slots: {
          title: {
            component: 'VTitle',
            bind: {
              text: 'Hello, vue-final-modal'
            }
          },
          default: {
            component: VContent,
            bind: {
              content:
                'Vue Final Modal is a renderless, stackable, detachable and lightweight modal component.'
            }
          }
        }
      })
    }
  }
}
</script>

App.vue 中註冊 modals-container 元件。

App.vue
<template>
  <div>
    ...
    <modals-container></modals-container>
  </div>
</template>
App.vue
<script>
import { ModalsContainer } from 'vue-final-modal'

export default {
  components: {
    ModalsContainer
  }
}
</script>

CustomModal.vue

基於 vue-final-modal 中的 VueFinalModal 寫一個叫做 CustomModal 的高階元件(HOC)

Source code

<template>
  <vue-final-modal v-bind="$attrs" classes="modal-container" content-class="modal-content" v-on="$listeners">
    <template v-slot="{ params }">
      <span class="modal__title">
        <slot name="title"></slot>
      </span>
      <div class="modal__content">
        <slot v-bind:params="params"></slot>
      </div>
      <div class="modal__action">
        <v-button @click="$emit('confirm', close)">confirm</v-button>
        <v-button @click="$emit('cancel', close)">cancel</v-button>
      </div>
      <button class="modal__close" @click="close">
        <mdi-close></mdi-close>
      </button>
    </template>
  </vue-final-modal>
</template>
<script>
export default {
  name: 'CustomModal',
  inheritAttrs: false,
  methods: {
    close() {
      this.$emit('input', false)
    }
  }
}
</script>
<style scoped>
::v-deep .modal-container {
  display: flex;
  justify-content: center;
  align-items: center;
}
::v-deep .modal-content {
  position: relative;
  display: flex;
  flex-direction: column;
  max-height: 90%;
  margin: 0 1rem;
  padding: 1rem;
  border: 1px solid #e2e8f0;
  border-radius: 0.25rem;
  background: #fff;
}
.modal__title {
  margin: 0 2rem 0 0;
  font-size: 1.5rem;
  font-weight: 700;
}
.modal__content {
  flex-grow: 1;
  overflow-y: auto;
}
.modal__action {
  display: flex;
  justify-content: center;
  align-items: center;
  flex-shrink: 0;
  padding: 1rem 0 0;
}
.modal__close {
  position: absolute;
  top: 0.5rem;
  right: 0.5rem;
}
</style>

<style scoped>
.dark-mode div::v-deep .modal-content {
  border-color: #2d3748;
  background-color: #1a202c;
}
</style>

VTitle.vue

<template>
  <div>{{ text }}</div>
</template>
<script>
export default {
  props: {
    text: {
      type: String,
      default: ''
    }
  }
}
</script>

VContent.vue

<template>
  <p>{{ content }}</p>
</template>
<script>
export default {
  props: {
    content: {
      type: String,
      default: ''
    }
  }
}
</script>
在 GitHub 上编辑此页面 Updated at Fri, Feb 3, 2023