javascript 可以从 vue 组件外部打开模态

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/50027090/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 08:52:46  来源:igfitidea点击:

Possible to open modal from outside the vue component

javascriptvue.js

提问by twoam

Is it possible to call a method from outside the component to make the component reuseable?

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

Right now I add my button to open the modal in a template slot:

现在我添加我的按钮以在模板插槽中打开模态:

index.php

index.php

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

Modal.vue

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>

I feel like there is an better option but I can't figure it out.

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

回答by roli roli

Yes,you can call a method from outside the component!

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

Parent component

父组件

<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>

Child component

子组件

<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

在这里查看它的实际效果

回答by Sphinx

I prefer using vue pluginat here (for single instance modal).

我更喜欢在这里使用vue 插件(对于单实例模式)。

Below is one demo:

下面是一个演示:

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/[email protected]/dist/vue.js"></script>
<div id="app">
    <button @click="openModal()">Open Modal!!!</button>
    <button @click="closeModal()">Close Modal!!!</button>
</div>

Rough Steps for vue-cli project:

vue-cli 项目的粗略步骤:

In ./plugins/SModal.js(follow the tutorial in official document to create one vue instance then add it into the document.body):

在./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

As the official document said, A Vue.js plugin should expose an install method. The method will be called with the Vue constructor as the first argument, along with possible options

正如官方文档所说,一个 Vue.js 插件应该公开一个安装方法。该方法将使用 Vue 构造函数作为第一个参数以及可能的选项来调用

In install.js(also you can move this method into main.js based on your design):

在 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
}

In main.js(finally uses your plugins):

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

import install from './install'
import * as plugins from './plugins'

Vue.use({ install }, {
  plugins
})

Finallyin your view/component, you can show/hide your modal like this:

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

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

回答by Ohgodwhy

Sure, accept a property to the modal component:

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

 props: ['open']

Then pass it in:

然后传入:

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

Then:

然后:

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

回答by Marco Pantaleoni

There is no (easy, supported) way to call a method in a component, but you can either modify a property in the child (eg. show) (see Passing Data to Child Components with Props) or use events (see Custom Events, $emitand $refs). Using events you could also use an event bus. The event based solution is of course better suited to more complex interactions.

没有(简单的,受支持的)方法来调用组件中的方法,但是您可以修改子组件中的属性(例如show)(请参阅使用道具将数据传递给子组件)或使用事件(请参阅自定义事件$发射$refs)。使用事件您也可以使用事件总线。基于事件的解决方案当然更适合更复杂的交互。

回答by Ashkan

I simply add v-on="$listeners"to the component in the children (modal.vue):

我只是添加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
        }
    },
    ...

Now you can open or close the modal from its parent easily:

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

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