首页 > 解决方案 > 在全局应用组件中渲染一个子组件

问题描述

我编写了一个对话框组件(全局)来显示带有叠加层的模态对话框,如弹出表单。

现在,对话框在使用它的组件内呈现。如果之后在 html 代码中存在相对位置的内容,这会导致内容重叠。

我希望它在最后呈现在根 App 组件中,以便我可以强制对话框始终位于所有其他内容之上

这是我不工作的解决方案:我尝试使用命名槽,希望它们也能在组件树中向后工作。不幸的是,他们似乎没有这样做。

任何人的解决方案如何做到这一点?

我的下一个想法是使用存储在应用程序组件中的额外组件进行渲染,并在全局状态中注册对话框。但是这个解决方案会非常复杂,看起来有点脏。

对话框组件:

<template v-slot:dialogs>
  <div class="dialog" :class="{'dialog--open': show, 'dialog--fullscreen': fullscreen }">
    <transition name="dialogfade" duration="300">
      <div class="dialog__overlay" v-if="show && !fullscreen" :key="'overlay'" @click="close"></div>
    </transition>
    <transition name="dialogzoom" duration="300">
      <div class="dialog__content" :style="{'max-width': maxWidth}" v-if="show" :key="'content'">
        <slot></slot>
      </div>
    </transition>
  </div>
</template>

<script>

  export default {
    name: "MyDialog",
    props: {show: {
        type: Boolean,
        default: false
      },
      persistent: {
        type: Boolean,
        default: true
      },
      fullscreen: {
        type: Boolean,
        default: false
      },
      maxWidth: {
        type: String,
        default: '600px'
      }
    },
    data: () => ({}),
    methods: {
      close() {
        if(!this.persistent) {
          this.$emit('close')
        }
      }
    }
  }
</script>

应用组件的模板:

<template>
  <div class="application">
    <div class="background">
      <div class="satellite"></div>
      <div class="car car-lr" :style="{ transform: `translateY(${car.x}px)`, left: adjustedLRLeft + '%' }" v-for="car in carsLR"></div>
    </div>
    <div class="content">
      <login v-if="!$store.state.user"/>
      <template v-else>
        <main-menu :show-menu="showMainMenu" @close="showMainMenu = false"/>
        <router-view/>
      </template>
      <notifications/>
      <div class="dialogs"><slot name="dialogs"></slot></div>
    </div>
  </div>
</template>

标签: vue.jsvue-component

解决方案


vuetify2 项目的帮助下,我找到了解决方案。对话框组件获得一个ref="dialogContent"属性,魔术发生在beforeMount函数中。

<template>
  <div class="dialog" ref="dialogContent" :class="{'dialog--open': show, 'dialog--fullscreen': fullscreen }">
    <transition name="dialogfade" duration="300">
      <div class="dialog__overlay" v-if="show && !fullscreen" :key="'overlay'" @click="close"></div>
    </transition>
    <transition name="dialogzoom" duration="300">
      <div class="dialog__content" :style="{'max-width': maxWidth}" v-if="show" :key="'content'">
        <slot></slot>
      </div>
    </transition>
  </div>
</template>

<script>

  export default {
    name: "MyDialog",
    props: {
      show: {
        type: Boolean,
        default: false
      },
      persistent: {
        type: Boolean,
        default: true
      },
      fullscreen: {
        type: Boolean,
        default: false
      },
      maxWidth: {
        type: String,
        default: '600px'
      }
    },
    data: () => ({}),
    methods: {
      close() {
        if (!this.persistent) {
          this.$emit('close')
        }
      }
    },
    beforeMount() {
      this.$nextTick(() => {
        const target = document.getElementById('dialogs');
        target.appendChild(
          this.$refs.dialogContent
        )
      })
    },
  }
</script>

推荐阅读