【问题标题】:trigger named component on click and manage click off element点击时触发命名组件并管理点击关闭元素
【发布时间】:2021-06-19 20:36:23
【问题描述】:

我正在创建一个Dropdown.vue 组件。它有一个名为 name 的道具,这样我就可以使用任何我想要的 div 通过点击事件来触发它。

比如我有一个元素<button @click="openDropdown('notifications-dropdown')">test</test>

然后我有组件:<dropdown name="notifications-dropdown"><div>content</div></dropdown>

点击事件是从全局 Vue 对象的方法中处理的:

methods: {
        openDropdown( name) {
            EventBus.$emit('dropdown-opened', name);
        }
    }

在实际的 Dropdown 组件上,我有 Mounted 方法:

EventBus.$on('dropdown-opened', (name) => {
 if (this.name == name) {
    this.active = true;
 }
});

这一切都很好,但是,我需要处理单击关闭元素功能,以便在未单击下拉列表时,active 布尔值更改为 false。

在下拉菜单中,我有:

methods: {
        closeDropdown(e) {
            if ( !this.$el.contains(e.target) ) {
                this.active = false;
            } 
        }
    }

mountedbeforeDestroy 中,我有:

document.removeEventListener('click', this.closeDropdown)

然后问题就变成了,在触发下拉菜单的初始按钮单击时也会触发事件单击关闭元素。所以下拉菜单打开并立即关闭

我怎样才能使它在触发下拉菜单的初始按钮点击时不会触发关闭e.target 点击closeDropdown()

但是,如果您再次单击触发器,我希望下拉菜单会关闭。

以下是完整代码:

Dropdown.vue

<template>
    <Transition name="fade-in-scale">
        <div @click.stop ref="thedropdown" v-if="active" class="dropdown-menu absolute bg-white border-l border-b border-r border-gray-200 top-100  w-72 right-0 md:w-84">
            <slot></slot>
        </div>
    </Transition>
</template>

<script>
// @ is an alias to /src

export default {

    name: 'Dropdown',
    data() {
        return {
            active: false
        }
    },
    props: {
        name: String
        
    },
    computed: {
      
    },
    mounted() {
        document.addEventListener('click', this.closeDropdown);

        EventBus.$on('dropdown-opened', (name) => {
            if (this.name == name) {
                this.active = !this.active;
            }
        });
       
    },

    beforeDestroy () {
        document.removeEventListener('click', this.closeDropdown)
    },

    watch: {
        
    },
    methods: {
        closeDropdown(e) {
            if ( !this.$el.contains(e.target) ) {
                this.active = false;
            } 
        }
    }
}
</script>


<style lang="scss" scoped>

    .dropdown-menu {
        top:calc(100% + 5px);
    }

    .fade-in-scale-enter-active,
    .fade-in-scale-leave-active {
        transition: all 0.12s;
        transform:scale(1) ;
        opacity:1;
    }
    .fade-in-scale-enter,
    .fade-in-scale-leave-to {
        transform: scale(0.90);
        opacity: 0;
    }
    
</style>

app.blade.php

<button @click="openDropdown('notifications-dropdown')" type="button" class="p-2 outline-none focus:outline-none"></button>
<dropdown name="notifications-dropdown"></dropdown>

app.js

require('./bootstrap');

/**
 * UI
 */
import Btn from './components/ui/Button.vue';
import Dropdown from './components/ui/Dropdown.vue';

const app = new Vue({
    el: '#app',
    components: {
       Btn,
       Dropdown
    },
    methods: {
        openDropdown( name) {
            EventBus.$emit('dropdown-opened', name);
        }
    }
});

【问题讨论】:

    标签: vue.js


    【解决方案1】:

    在打开按钮的@click 上使用.stop event modifier,这样事件的传播就会停止并且不会到达文档的click 处理程序:

    <button @click.stop="openDropdown(...)">
    

    demo

    还要注意Dropdown.vue 中的内存泄漏,因为它不会从事件总线中删除事件侦听器。通过为openDropdown 声明一个组件方法并使用它在mounted 中添加事件侦听器并在beforeDestroy 中删除它,可以轻松解决此问题:

    export default {
        mounted() {
            EventBus.$on('dropdown-opened', this.openDropdown);
        },
    
        beforeDestroy () {
            EventBus.$off('dropdown-opened', this.openDropdown)
        },
    
        methods: {
            openDropdown(name) {/*...*/}
        }
    }
    

    【讨论】:

    • 完美!非常感谢你。我担心我不得不重新考虑我是如何做事的。我将不得不研究内存泄漏和 Vue.js。我添加了 mount 和 beforeDestroy 方法。谢谢谢谢!
    • 为什么要使用事件总线来打开下拉菜单?我只想在下拉列表中放置一个模板引用来调用其openDropdown 方法。
    • 最初我为每个下拉菜单创建一个单独的组件。我将返回并重构组件,以便元素可以简单地调用使用插槽的下拉列表。你能给我举个例子来说明你的意思吗?我只使用了总线,因为我不知道全局调用组件的特定实例的另一种方法。对 vue 还是很陌生并且有坏习惯
    • 范围插槽绝对是要走的路。
    • 快速重构是让父级在下拉列表中放置一个模板引用 (&lt;dropdown ref="dropdown"&gt;),并从打开按钮的 click-handler (&lt;button @click="$refs.dropdown.openDropdown()"&gt;Open&lt;/button&gt;) 调用其 openDropdown 方法)
    猜你喜欢
    • 2021-05-11
    • 1970-01-01
    • 2012-12-07
    • 2020-10-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-06
    相关资源
    最近更新 更多