Angular2点击外面的关闭下拉菜单,是否有最简单的方法? [英] Angular2 Close dropdown on click outside, is there an easiest way?

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

问题描述

我想关闭我的登录菜单下拉菜单,当用户点击该下拉列表以外的任何地方,我想使用Angular2和angular2方法...



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



这是我的实现: p>

下拉组件:



这是我的下拉菜单的组件:




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

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

  • 每次点击组件触发 onClick()方法,它只是阻止事件冒泡到顶部(和应用程序组件)



这里是代码

  export class UserMenuComponent {

_isVisible:boolean = false;
_subscriptions:订阅< any> = null;

构造函数(公共主题: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;
}
}



应用程序组件:



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




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



代码:

 导出类AppComponent {

构造函数(公共主题:SubjectsService){
document.addEventListener('click',()=> this.onClick());
}
onClick(){
this.subjects.userMenu.next({});
}
}



什么麻烦我:




  1. 我不觉得有一个全局主题作为这些组件之间的连接器的想法

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


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

    • 显示下拉菜单,它立即订阅用户菜单主题

    • 点击事件向应用程序组件冒泡,并捕获

    • 应用程序组件在 userMenu 上发出一个事件

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

    • 最后,下拉列表永远不会


超时延迟订阅结束当前的JS转向解决问题,但在我看来非常优雅的方式。



如果你知道更清洁,更好,更聪明,更快或更强的解决方案,请让我知道:)!

解决方案

您可以使用(文档:点击) event: p

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

onClick(event){
if(!this._eref.nativeElement.contains(event.target))//或一些类似的检查
doSomething();
}
}

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




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 :) !

Here is my implementation:

The dropdown component:

This is the component for my dropdown:

  • 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 app component)

Here is the code

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

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

Here is the code:

export class AppComponent {

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

What bother me:

  1. I do not feel really confortable 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 get catched
    • the app 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.

This set timeout delay the subscription to the end of the current JS 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 :) !

解决方案

You can use (document:click) event:

@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();
  }
}

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

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

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