【问题标题】:Possible to open modal from outside the vue component可以从 vue 组件外部打开模态
【发布时间】:2020-10-16 04:56:53
【问题描述】:

是否可以从组件外部调用方法使组件可重用?

现在我添加按钮以在模板槽中打开模式:

index.php

<modal>
    <template slot="button">
        <button class="btn">Open modal</button>
    </template>
    Some modal text
</modal>

Modal.vue

<template>
    <div>
        <div @click="showModal"><slot name="button"></slot></div>
        <div v-if="showingModal"><slot></slot></div>
    </div>
</template>

<script>
    export default {

        data () {
            return {
                showingModal: false,
            }
        },

        methods: {
            showModal() {
                this.showingModal = true;
            },
        }
    }
</script>

我觉得有更好的选择,但我想不通。

【问题讨论】:

    标签: javascript vue.js


    【解决方案1】:

    是的,您可以从组件外部调用方法!

    父组件

    <template>
     <div>
       <modal ref="modal"></modal>
       <button @click="openModal">Open Modal</button>
     </div>
    </template>
    
    <script>
      import modal from './child.vue'
      export default {
        components: { modal }
        methods: {
         openModal() { this.$refs.modal.show() }//executing the show method of child
        }
      }
    </script>
    

    子组件

    <template>
      <div v-if="showModal">
        <div id="modal">
          <p>Hello i am a modal
          </p>
          <button @click="hide">Close</button>
        </div> 
      </div>
    </template>
    
    <script>
     export default {
       data() {
         return {
          showModal: false
         }
       },
       methods: {
         show() {
          this.showModal = true
         },
         hide(){
          this.showModal = false
         }
       }
     }
    </script>
    

    See it in action here

    【讨论】:

      【解决方案2】:

      将您的modal 组件实例放入Vue.prototype,然后在您可以访问Vue 实例上下文的任何地方调用show/hide

      以下是一个演示:

      let vm = null // the instance for your Vue modal
      let timeout = null //async/delay popup
      
      const SModal = {
        isActive: false,
      
        show ({
          delay = 500,
          message = '',
          customClass = 'my-modal-class'
        } = {}) {
          if (this.isActive) {
            vm && vm.$forceUpdate()
            return
          }
      
          timeout = setTimeout(() => {
            timeout = null
      
            const node = document.createElement('div')
            document.body.appendChild(node)
            let staticClass = ''
            vm = new this.__Vue({
              name: 's-modal',
              el: node,
              render (h) { // uses render() which is a closer-to-the-compiler alternative to templates
                return h('div', {
                  staticClass,
                  'class': customClass,
                  domProps: {
                    innerHTML: message
                  }
                })
              }
            })
          }, delay)
      
          this.isActive = true
        },
        hide () {
          if (!this.isActive) {
            return
          }
      
          if (timeout) {
            clearTimeout(timeout)
            timeout = null
          } else {
            vm.$destroy()
            document.body.removeChild(vm.$el)
            vm = null
          }
      
          this.isActive = false
        },
      
        __Vue: null,
        __installed: false,
        install ({ $my, Vue }) {
          if (this.__installed) { return }
          this.__installed = true
          $my.SModal = SModal // added your SModal object to $my
          this.__Vue = Vue //get the Vue constructor
        }
      }
      
      let installFunc = function (_Vue, opts = {}) {
        if (this.__installed) {
          return
        }
        this.__installed = true
        const $my = {
          'memo': 'I am a plugin management.'
        }
        if (opts.plugins) {
          Object.keys(opts.plugins).forEach(key => {
            const p = opts.plugins[key]
            if (typeof p.install === 'function') {
              p.install({ $my, Vue: _Vue })
            }
          })
        }
        _Vue.prototype.$my = $my
      }
      
      Vue.use(installFunc, {
        plugins: [SModal]
      })
      
      app = new Vue({
        el: "#app",
        data: {
          'test 1': 'Cat in Boots'
        },
        methods: {
          openModal: function () {
            this.$my.SModal.show({'message':'test', 'delay':1000})
          },
          closeModal: function () {
            this.$my.SModal.hide()
          }
        }
      })
      .my-modal-class {
        position:absolute;
        top:50px;
        left:20px;
        width:100px;
        height:100px;
        background-color:red;
        z-index:9999;
      }
      <script src="https://unpkg.com/vue@2.5.16/dist/vue.js"></script>
      <div id="app">
          <button @click="openModal()">Open Modal!!!</button>
          <button @click="closeModal()">Close Modal!!!</button>
      </div>

      vue-cli 项目的粗略步骤:

      在./plugins/SModal.js中(按照官方文档中的教程创建一个vue实例然后添加到document.body):

      let vm = null // the instance for your Vue modal
      let timeout = null //async/delay popup
      
      const SModal = {
        isActive: false,
      
        show ({
          delay = 500,
          message = '',
          customClass = ''
        } = {}) {
          if (this.isActive) {
            vm && vm.$forceUpdate()
            return
          }
      
          timeout = setTimeout(() => {
            timeout = null
      
            const node = document.createElement('div')
            document.body.appendChild(node)
      
            vm = new this.__Vue({
              name: 's-modal',
              el: node,
              render (h) { // uses render() which is a closer-to-the-compiler alternative to templates
                return h('div', {
                  staticClass,
                  'class': props.customClass
                })
              }
            })
          }, delay)
      
          this.isActive = true
        },
        hide () {
          if (!this.isActive) {
            return
          }
      
          if (timeout) {
            clearTimeout(timeout)
            timeout = null
          } else {
            vm.$destroy()
            document.body.removeChild(vm.$el)
            vm = null
          }
      
          this.isActive = false
        },
      
        __Vue: null,
        __installed: false,
        install ({ $my, Vue }) {
          if (this.__installed) { return }
          this.__installed = true
          $my.SModal = SModal // added your SModal object to $my
          this.__Vue = Vue //get the Vue constructor
        }
      }
      
      export default SModal
      

      作为the official document said,Vue.js 插件应该公开一个安装方法。该方法将使用 Vue 构造函数作为第一个参数以及可能的选项来调用

      在 install.js 中(您也可以根据您的设计将此方法移动到 main.js 中):

      // loop all plugins under the folder ./plugins/, then install it.
      export default function (_Vue, opts = {}) {
        if (this.__installed) {
          return
        }
        this.__installed = true
        const $my = {
          'memo': 'I am a plugin management.'
        }
        if (opts.plugins) {
          Object.keys(opts.plugins).forEach(key => {
            const p = opts.plugins[key]
            if (typeof p.install === 'function') {
              p.install({ $my, Vue: _Vue })
            }
          })
        }
      
        _Vue.prototype.$my = $my
      }
      

      在 main.js 中(最终使用您的插件):

      import install from './install'
      import * as plugins from './plugins'
      
      Vue.use({ install }, {
        plugins
      })
      

      最后在您的视图/组件中,您可以像这样显示/隐藏您的模式:

      this.$my.SModal.show()
      this.$my.SModal.hide()
      

      【讨论】:

        【解决方案3】:

        当然,接受模态组件的属性:

         props: ['open']
        

        然后传入:

        <modal :open="boolToOpenModal"> ... </modal>
        

        然后:

        <div v-if="showingModal || open"><slot></slot></div>
        

        【讨论】:

          【解决方案4】:

          没有(简单的,受支持的)方法来调用组件中的方法,但是您可以修改子中的属性(例如show)(请参阅Passing Data to Child Components with Props)或使用事件(请参阅@987654322 @、$emit$refs)。使用事件您也可以使用event bus。基于事件的解决方案当然更适合更复杂的交互。

          【讨论】:

            【解决方案5】:

            我只是将v-on="$listeners" 添加到子组件(modal.vue)中:

            // modal.vue
            <template>
               <div :show="show" v-on="$listeners">
                 ...
               </div>
            </template>
            
            <script>
            
            export default {
                props: {
                    show: {
                        type: Boolean,
                        default: false
                    }
                },
                ...
            

            现在您可以轻松地从其父模式打开或关闭模式:

            //parent.vue
            <modal @close="showModal = false" :show="showModal" />
            

            【讨论】:

              猜你喜欢
              • 2019-03-17
              • 1970-01-01
              • 1970-01-01
              • 2017-09-15
              • 1970-01-01
              • 1970-01-01
              • 2020-09-11
              • 1970-01-01
              • 2019-01-06
              相关资源
              最近更新 更多