这div将在页面作为东面板上被动态地示出的,当open panel按钮被点击。bool showEastPanel变量是用于打开和关闭东部面板的变量。我试图用来(clickoutside)关闭面板(设置showEastPanel为false),但是打开的面板首先在Angular挂钩上运行,并且面板设置为true,然后设置为false,并且面板不显示。有什么办法使“ clickoutside”范围不包括按钮?
<div [ngClass]="{'d-none': !showEastPanel, 'east-panel-container': showEastPanel}" (clickOutside)="ClosePanel()">
<div id="east-panel">
<ng-template #eastPanel></ng-template>
</div>
Run Code Online (Sandbox Code Playgroud)
<button (click)="ShowPanel()">Open Panel</button>
Run Code Online (Sandbox Code Playgroud)
Emr*_*son 17
我喜欢苏杰的回答。如果您希望创建一个指令(在多个组件中使用)。我就是这样做的。
import {
Directive,
EventEmitter,
HostListener,
Output,
ElementRef,
} from '@angular/core';
@Directive({
selector: '[outsideClick]',
})
export class OutsideClickDirective {
@Output()
outsideClick: EventEmitter<MouseEvent> = new EventEmitter();
@HostListener('document:mousedown', ['$event'])
onClick(event: MouseEvent): void {
if (!this.elementRef.nativeElement.contains(event.target)) {
this.outsideClick.emit(event);
}
}
constructor(private elementRef: ElementRef) {}
}
Run Code Online (Sandbox Code Playgroud)
然后您可以像这样使用该指令:
<div class="menu" *ngIf="isMenuOpen" (outsideClick)="isMenuOpen = false" outsideClick #menu>
I'm the menu. Click outside to close me
</div>
Run Code Online (Sandbox Code Playgroud)
Suj*_*jay 15
你可以做这样的事情
@HostListener('document:mousedown', ['$event'])
onGlobalClick(event): void {
if (!this.elementRef.nativeElement.contains(event.target)) {
// clicked outside => close dropdown list
this.isOpen = false;
}
}
Run Code Online (Sandbox Code Playgroud)
并为面板使用 *ngIf=isOpen
kys*_*elm 10
我想添加帮助我获得正确结果的解决方案。
当使用嵌入元素并且您想要检测父元素的点击时, event.target 提供对基本子元素的引用。
HTML
<div #toggleButton (click)="toggleMenu()">
<u>Toggle Menu</u>
<span class="some-icon"></span>
</div>
<div #menu class="menu" *ngIf="isMenuOpen">
<h1>I'm the menu.</h1>
<div>
I have some complex content containing multiple children.
<i>Click outside to close me</i>
</div>
</div>
Run Code Online (Sandbox Code Playgroud)
我单击“切换菜单”文本,event.target 返回对'u'元素而不是#toggleButton div的引用。
对于这种情况,我使用了 M98 的解决方案,包括 Renderer2,但将条件更改为 Sujay 的回答中的条件。
ToggleButton.nativeElement.contains(e.target)即使单击事件的目标在 nativeElement 的子级中也返回true,从而解决了问题。
组件.ts
export class AppComponent {
/**
* This is the toogle button element, look at HTML and see its definition
*/
@ViewChild('toggleButton') toggleButton: ElementRef;
@ViewChild('menu') menu: ElementRef;
isMenuOpen = false;
constructor(private renderer: Renderer2) {
/**
* This events get called by all clicks on the page
*/
this.renderer.listen('window', 'click',(e:Event)=>{
/**
* Only run when toggleButton is not clicked
* If we don't check this, all clicks (even on the toggle button) gets into this
* section which in the result we might never see the menu open!
* And the menu itself is checked here, and it's where we check just outside of
* the menu and button the condition abbove must close the menu
*/
if(!this.toggleButton.nativeElement.contains(e.target) && !this.menu.nativeElement.contains(e.target)) {
this.isMenuOpen=false;
}
});
}
toggleMenu() {
this.isMenuOpen = !this.isMenuOpen;
}
}
Run Code Online (Sandbox Code Playgroud)
我将通过使用Angular推荐的方法来做到这一点,该方法在没有DOM访问的环境中也很容易开发应用程序,我的意思是Renderer 2类是Angular提供的一种抽象形式,该形式是一种服务,该服务允许操作应用程序的元素而不必直接触摸DOM。
在这种方法中,您需要注入Renderer2到组件构造函数中,这Renderer2使我们能够listen优雅地触发事件。它只是需要的元素你要听上作为可成为第一个参数window,document,body或任何其他元素的参考。对于第二个参数,它采用了我们将要监听的事件(在本例中为)click,而第三个参数实际上是我们通过箭头函数执行的回调函数。
this.renderer.listen('window', 'click',(e:Event)=>{ // your code here})
Run Code Online (Sandbox Code Playgroud)
解决方案的其余部分很容易,您只需要设置一个布尔标志即可保持菜单(或面板)状态的可见性,我们应该做的就是false在菜单外单击该标志时将其分配给该标志。
这是Stackblitz Demo的链接
的HTML
<button #toggleButton (click)="toggleMenu()"> Toggle Menu</button>
<div class="menu" *ngIf="isMenuOpen" #menu>
I'm the menu. Click outside to close me
</div>
Run Code Online (Sandbox Code Playgroud)
app.component.ts
export class AppComponent {
/**
* This is the toogle button elemenbt, look at HTML and see its defination
*/
@ViewChild('toggleButton') toggleButton: ElementRef;
@ViewChild('menu') menu: ElementRef;
constructor(private renderer: Renderer2) {
/**
* This events get called by all clicks on the page
*/
this.renderer.listen('window', 'click',(e:Event)=>{
/**
* Only run when toggleButton is not clicked
* If we don't check this, all clicks (even on the toggle button) gets into this
* section which in the result we might never see the menu open!
* And the menu itself is checked here, and it's where we check just outside of
* the menu and button the condition abbove must close the menu
*/
if(e.target !== this.toggleButton.nativeElement && e.target!==this.menu.nativeElement){
this.isMenuOpen=false;
}
});
}
isMenuOpen = false;
toggleMenu() {
this.isMenuOpen = !this.isMenuOpen;
}
}
Run Code Online (Sandbox Code Playgroud)
这是一个可重用的指令,它还涵盖了元素位于 ngIf 内部的情况:
import { Directive, ElementRef, Optional, Inject, Output, EventEmitter, OnInit, OnDestroy } from '@angular/core';
import { fromEvent, Subscription } from 'rxjs';
import { DOCUMENT } from '@angular/common';
import { filter } from 'rxjs/operators';
@Directive({
selector: '[outsideClick]',
})
export class OutsideClickDirective implements OnInit, OnDestroy {
@Output('outsideClick') outsideClick = new EventEmitter<MouseEvent>();
private subscription: Subscription;
constructor(private element: ElementRef, @Optional() @Inject(DOCUMENT) private document: any) {}
ngOnInit() {
setTimeout(() => {
this.subscription = fromEvent<MouseEvent>(this.document, 'click')
.pipe(
filter(event => {
const clickTarget = event.target as HTMLElement;
return !this.isOrContainsClickTarget(this.element.nativeElement, clickTarget);
}),
)
.subscribe(event => this.outsideClick.emit());
}, 0);
}
private isOrContainsClickTarget(element: HTMLElement, clickTarget: HTMLElement) {
return element == clickTarget || element.contains(clickTarget);
}
ngOnDestroy() {
if (this.subscription) this.subscription.unsubscribe();
}
}
Run Code Online (Sandbox Code Playgroud)
感谢https://github.com/ngez/platform,我从中得到了大部分逻辑。
我缺少的是 setTimeout(..., 0),它确保在使用该指令的组件呈现后安排检查。
有用的链接:
小智 6
更简化的代码与演示:StackBlitz
我制作了一个通用功能,可以在外部单击时关闭菜单,并在特定元素上触发单击时防止关闭。
超文本标记语言
<button (click)="toggleMenu(); preventCloseOnClick()">Toggle Menu</button>
<ul (click)="preventCloseOnClick()" *ngIf="menuOpen">
<li>Menu 1</li>
<li>Menu 2</li>
<li>Menu 3</li>
<li>Menu 4</li>
<li>Menu 5</li>
</ul>
Run Code Online (Sandbox Code Playgroud)
TS
import { Component, VERSION, Renderer2 } from '@angular/core';
export class AppComponent {
menuOpen: boolean = false;
menuBtnClick: boolean = false;
constructor(private renderer: Renderer2) {
this.renderer.listen('window', 'click', (e: Event) => {
if (!this.menuBtnClick) {
this.menuOpen = false;
}
this.menuBtnClick = false;
});
}
toggleMenu() {
this.menuOpen = !this.menuOpen;
}
preventCloseOnClick() {
this.menuBtnClick = true;
}
}
Run Code Online (Sandbox Code Playgroud)