Angular 角度2+&引用;单击“外部”;由2个组件组成,而不仅仅是1个

Angular 角度2+&引用;单击“外部”;由2个组件组成,而不仅仅是1个,angular,event-handling,Angular,Event Handling,我有一个多组件侧边栏切换,应用程序/按钮/侧边栏都在自己的组件中,按钮/侧边栏是应用程序的子级(或者至少在应用程序组件中加载,假设这是子级) 在Angular的跨组件通信方面仍然没有达到最快的速度,我在本例中使用了一个服务 我想要的是:如果没有点击组件按钮或边栏(这两个组件之外的任何东西)。。。折叠侧边栏 目前,我正在切换服务中“可见”变量的布尔状态,以展开/折叠nav 我尝试过各种方法,但收效甚微。有什么方法可以做到这一点?或者至少在某个我可以阅读的地方靠近并从那里获取信息 代码:您应该使用指

我有一个多组件侧边栏切换,应用程序/按钮/侧边栏都在自己的组件中,按钮/侧边栏是应用程序的子级(或者至少在应用程序组件中加载,假设这是子级)

在Angular的跨组件通信方面仍然没有达到最快的速度,我在本例中使用了一个服务

我想要的是:如果没有点击组件按钮或边栏(这两个组件之外的任何东西)。。。折叠侧边栏

目前,我正在切换服务中“可见”变量的布尔状态,以展开/折叠nav

我尝试过各种方法,但收效甚微。有什么方法可以做到这一点?或者至少在某个我可以阅读的地方靠近并从那里获取信息


代码:

您应该使用指令来收听外部面板的单击:

import { Directive, ElementRef, EventEmitter, HostListener, Output } from '@angular/core';

@Directive({
    selector: '[clickOutside]'
})
export class ClickOutsideDirective {
    @Output() clickOutside: EventEmitter<Event> = new EventEmitter<Event>();

    constructor(private elementRef: ElementRef) { }

    @HostListener('document:mousedown', ['$event'])
    onDocumentClick(event: Event) {
        if (!this.isClickInElement(event)) {
            this.clickOutside.emit(event);
        }
    }

    private isClickInElement(event: Event): boolean {
        return this.elementRef.nativeElement.contains(event.target);
    }
}
import{Directive,ElementRef,EventEmitter,HostListener,Output}来自“@angular/core”;
@指示({
选择器:“[单击外部]”
})
导出类ClickOutside指令{
@输出()单击外部:EventEmitter=新的EventEmitter();
构造函数(私有elementRef:elementRef){}
@HostListener('document:mousedown',['$event']))
onDocumentClick(事件:事件){
如果(!this.isclickinement(事件)){
点击outside.emit(事件);
}
}
私有isClickineElement(事件:事件):布尔值{
返回此.elementRef.nativeElement.contains(event.target);
}
}
现在,您可以在菜单中附加该指令并绑定到其输出,然后调用该方法来折叠侧栏或执行操作。例:

<ol (clickOutside)="collapseSideBar()">

如果
文档:单击事件处理程序切换
可见
标志,则当前代码有效:

@HostListener("document:click", ["$event"])
onClick(event) {
  this.visible = this._ref.nativeElement.contains(event.target);
}
要防止按钮单击达到文档级别,请停止事件传播:

<toggle-sidebar (click)="$event.stopPropagation()"></toggle-sidebar>


有关演示,请参阅。

您可以在侧边栏和按钮中处理
单击外部。如果两者都在外部单击,请关闭侧边栏。您可以通过在两个子组件中使用@Output/EventEmitter并在
app.component.ts
文件中收听它们来实现相同的效果

因此,总结一下:

  • 选中侧边栏中的
    单击外部
    ,并将其发送给父级,即
    应用组件.ts
  • 选中
    点击切换按钮中的外部
    ,并将其发送给父级,即
    应用组件.ts
  • app.component.ts
    中,处理上述发射并执行检查:
    • 当在外部单击切换按钮,在外部单击sidenav,并且sidenav打开时,我们应该关闭sidenav
    • 当在内部单击切换按钮时,功能上将在外部单击sidenav。但是,我们必须将sidenav更改为在内部单击。因此,我们将手动更改该标志。否则,如果您在sidenav中单击但在外部单击切换按钮,sidenav将关闭

  • 考虑到上述场景,我已经编辑了stackblitz。

    看看Material的sidenav是如何做到这一点的:基本上,您希望在页面上创建一个绝对定位的覆盖,并呈现其中的sidenav元素。您可以将叠加本身上的单击事件设置为sidenav的“模糊”
    @angular/cdk
    有一个用于此的覆盖。非常有趣,谢谢!我将对此进行研究。我已经尝试过,其工作原理与当前的设置类似,切换的“按钮”位于组件外部,因此实际上不会切换。可能是将其添加到包含侧边栏的更高级别。。并将事件从它发送到子对象以隐藏?您可以在侧栏和按钮中处理
    clickOutSide
    。如果两者都在外部单击,请关闭侧边栏。您可以在两个子组件中通过@Output/EventEmitter获得相同的结果,并在
    app.component.ts
    文件中收听它们。@robert,因为它很长,我在下面添加了它作为答案。请注意使用
    Renderer2
    而不是Angular中的直接DOM操作实际上这是我已经尝试实现的,只是不确定如何在Angular中正确调用e.stopPropagination(),与javascript有点不同。但这是有道理的。幸亏有这么多方法可以跨组件处理事件。很难做到这一点。快速提问:这似乎更直接、更条块化,尽管它需要更多的代码来适应,也就是说:为什么这是一种比当前选择的答案更好的方法?为了透明起见,我是一个css/html/js开发人员,而不是ts/angular开发人员。我只是想抓住它。我可以看出这是一个可以更容易扩展的东西,但我不确定我是否掌握了这个方法的全部范围。第1部分-@darcher我不确定这是否比公认的答案更好。这只是另一种方法。就我个人而言,我更喜欢这样,因为点击事件只停留在组件内部,它不会泄漏到外部,我们正在处理我们想要在组件外部共享的内容。比方说,在接受的答案中,为了处理单击,您将创建一个函数
    toggleClicked(ev:Event){///您的逻辑将在ev.stopPropagation()之前执行,然后ev.stopPropagation()被调用//}
    ,并且在您的模板
    。第2部分(这与第1部分相关)-因此,在函数
    toggleClicked
    中,您正在传递整个单击事件。要查看它有多少属性,请参见此处:或。正如您所看到的,它有很多数据,我们正在将这些数据从HTML传递到组件。这就是原因,我会避免解决方案,其中有整个点击事件对象。即使Angular的官方文档建议不要使用它,请在此处签出:。希望,这有帮助。谢谢!这是一个很好的解释和阅读。我一定会看一看,比较一下。这很有帮助。