如何关闭外部点击的下拉菜单? [英] How can I close a dropdown on click outside?

查看:156
本文介绍了如何关闭外部点击的下拉菜单?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当用户点击该下拉菜单之外的任何地方时,我想关闭我的登录菜单下拉菜单,我想用Angular2和Angular2方法来做...

I would like to close my login menu dropdown when the user click anywhere outside of that dropdown, and I'd like to do that with Angular2 and with the Angular2 "approach"...

我已经实施了一个解决方案,但我真的对它没有信心。我认为必须有一种最简单的方法来实现相同的结果,所以如果你有任何想法......让我们讨论:)!

I have implemented a solution, but I really do not feel confident with it. I think there must be an easiest way to achieve the same result, so if you have any ideas ... let's discuss :) !

这是我的实现:

这是我的下拉列表的组件:

This is the component for my dropdown:


  • 每次将此组件设置为可见时(例如:当用户单击按钮显示它时),它会订阅全局rxjs主题 userMenu 存储在 SubjectsService 中。

  • 每次隐藏时,都会取消订阅此主题。

  • 每次点击 此组件的模板中的每次点击都会触发 onClick()方法,该方法只会阻止事件冒泡到顶部(和应用程序组件)

  • Every time this component it set to visible, (For example: when the user click on a button to display it) it subscribe to a "global" rxjs subject userMenu stored within the SubjectsService.
  • And every time it is hidden, it unsubscribe to this subject.
  • Every click anywhere within the template of this component trigger the onClick() method, which just stop event bubbling to the top (and the application component)

以下是代码

export class UserMenuComponent {

    _isVisible: boolean = false;
    _subscriptions: Subscription<any> = null;

    constructor(public subjects: SubjectsService) {
    }

    onClick(event) {
        event.stopPropagation();
    }

    set isVisible(v) {
        if( v ){
            setTimeout( () => {
this._subscriptions =  this.subjects.userMenu.subscribe((e) => {
                       this.isVisible = false;
                       })
            }, 0);
        } else {
            this._subscriptions.unsubscribe();
        }
        this._isVisible = v;
    }

    get isVisible() {
        return this._isVisible;
    }
}



应用程序组件:



另一方面,有应用程序组件(它是下拉组件的父级):

The application component:

On the other hand, there is the application component (which is a parent of the dropdown component):


  • 此组件捕获每个单击事件并在相同的rxjs上发出主题( userMenu

  • This component catch every click event and emit on the same rxjs Subject (userMenu)

这是代码:

export class AppComponent {

    constructor( public subjects: SubjectsService) {
        document.addEventListener('click', () => this.onClick());
    }
    onClick( ) {
        this.subjects.userMenu.next({});
    }
}



困扰我的是:



What bother me:


  1. 我觉得让一个全局主题充当这些组件之间的连接器的想法让我感到很自在。

  2. setTimeout :这是必需的,因为如果用户点击显示下拉列表的按钮,则会发生以下情况:


    • 用户点击按钮(不是下拉组件的一部分)来显示下拉列表。

    • 显示下拉列表,立即订阅userMenu主题

    • 点击事件冒泡到应用程序组件并被捕获

    • 应用程序组件在 userMenu 主题

    • 下拉组件在 userMenu 上捕获此操作并隐藏下拉列表。

    • 最后下拉列表是从未显示过。

  1. I do not feel really comfortable with the idea of having a global Subject that act as the connector between those components.
  2. The setTimeout: This is needed because here is what happen otherwise if the user click on the button that show the dropdown:
    • The user click on the button (which is not a part of the dropdown component) to show the dropdown.
    • The dropdown is displayed and it immediately subscribe to the userMenu subject.
    • The click event bubble up to the app component and gets caught
    • The application component emit an event on the userMenu subject
    • The dropdown component catch this action on userMenu and hide the dropdown.
    • At the end the dropdown is never displayed.

T他的设置超时延迟了当前JavaScript代码转换结束的订阅,解决了这个问题,但在我看来,这是一种非常优雅的方式。

This set timeout delay the subscription to the end of the current JavaScript code turn which solve the problem, but in a very in elegant way in my opinion.

如果你知道更干净,更好,更智能,更快或更强的解决方案,请让我知道:)!

If you know cleaner, better, smarter, faster or stronger solutions, please let me know :) !

推荐答案

你可以使用(文件:点击)事件:

@Component({
  host: {
    '(document:click)': 'onClick($event)',
  },
})
class SomeComponent() {
  constructor(private _eref: ElementRef) { }

  onClick(event) {
   if (!this._eref.nativeElement.contains(event.target)) // or some similar check
     doSomething();
  }
}

另一种方法是将自定义事件创建为指令。查看Ben Nadel的这些帖子:

Another approach is to create custom event as a directive. Check out these posts by Ben Nadel:

  • tracking-click-events-outside-the-current-component
  • selectors-and-outputs-can-have-the-same-name
  • DirectiveMetadata
  • Host Binding

这篇关于如何关闭外部点击的下拉菜单?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆