检测点击 Angular 组件外部
在Angular组件中检测点击外部的问题可能会导致性能问题,特别是在构建自定义下拉组件时,如果在表单中创建了多个实例,使用绑定到document的点击事件会有可见的性能影响。
解决方法是在主要的应用程序组件中只添加一个()绑定到document的点击事件。该事件应该将点击的目标元素的值推送到存储在全局实用服务中的公共subject中。
受兴趣的人应该订阅我们实用程序服务的公共subject,并在组件被销毁时取消订阅。
以下是一个示例代码:
({ selector: 'app-root', template: '<router-outlet></router-outlet>' }) export class AppComponent { constructor(private utilitiesService: UtilitiesService) {} @HostListener('document:click', ['$event']) documentClick(event: any): void { this.utilitiesService.documentClickedTarget.next(event.target) } } ({ providedIn: 'root' }) export class UtilitiesService { documentClickedTarget: Subject<HTMLElement> = new Subject<HTMLElement>() } export class AnotherComponent implements OnInit { @ViewChild('somePopup', { read: ElementRef, static: false }) somePopup: ElementRef constructor(private utilitiesService: UtilitiesService) { } ngOnInit() { this.utilitiesService.documentClickedTarget .subscribe(target => this.documentClickListener(target)) } documentClickListener(target: any): void { if (this.somePopup.nativeElement.contains(target)) // 点击在内部 else // 点击在外部 } }
这种方法允许进行许多优化,例如在提到的示例中。确保在组件销毁时进行取消订阅操作。使用takeUntil()或Subscription.add()来实现取消订阅。
是否有一种方法可以在有订阅者对事件感兴趣时才激活HostListener?
对于我来说,每次点击时都会触发tick()两次,即使没有订阅它。我找到了一个使用Renderer2的解决方法,它也表现出相同的行为,但是我将其包装在runOutsideAngular()中。现在,我的下拉组件不会触发不必要的变更检测。
参考链接:
- stackoverflow.com/a/60014879/702951
在Angular组件中检测点击外部的问题是一个常见的需求。当用户点击组件外部的区域时,我们希望能够触发相应的事件。下面我们将介绍问题的出现原因以及解决方法。
问题的出现原因:
在Angular中,当我们使用ngIf指令来动态添加或移除DOM元素时,如果我们想要检测点击组件外部的事件,就会遇到问题。具体来说,当我们点击一个通过ngIf指令移除的元素时,事件并不会被触发。
解决方法:
为了解决这个问题,可以使用以下的解决方案:
private wasInside = false; clickInside() { this.text = "clicked inside"; this.wasInside = true; } @HostListener('document:click') clickoutside() { if (!this.wasInside) { this.text = "clicked outside"; } this.wasInside = false; }
在这个解决方案中,我们首先定义了一个私有变量`wasInside`,用于标记点击事件是否发生在组件内部。接着,我们使用`@HostListener`装饰器监听`document:click`事件,当点击事件发生时,会调用`clickoutside`方法。在该方法中,我们首先判断`wasInside`的值,如果为false,则说明点击事件发生在组件外部,我们可以执行相应的逻辑。
需要注意的是,我们需要在组件的构造函数中引入`HostListener`装饰器:
import { Component, HostListener } from '@angular/core';
这样,我们就可以在Angular组件中成功地检测到点击组件外部的事件了。
需要特别注意的是,在上述解决方案中,我们使用了`@HostListener`装饰器来监听`document:click`事件。这样可以确保`clickInside`方法在`clickoutside`方法之前被调用,即使点击事件发生在组件内部元素上。
通过使用这个解决方案,我们可以在Angular中轻松地检测点击组件外部的事件,无论是使用ngIf指令动态添加或移除DOM元素,还是在组件内部进行动态更新。
在上面的代码中,我们有一个Angular组件AnotherComponent,它包含一个div元素,显示一个文本。该组件还有一个clickout方法,用于检测点击事件是否发生在该组件之外。如果点击事件发生在组件之内,文本将被设置为"clicked inside";如果点击事件发生在组件之外,文本将被设置为"clicked outside"。
然而,这种方法在触发元素中存在由ngIf控制的元素时无法正常工作,因为ngIf从DOM中移除元素会在点击事件之前发生。因此,无法正确检测到点击事件是否发生在组件之外。
解决这个问题的方法是使用HostListener装饰器,监听document的点击事件。在点击事件发生时,通过检查点击的目标元素是否包含在组件的根元素中,来判断点击事件是否发生在组件之内。这样,无论是否存在由ngIf控制的元素,都可以正确地检测点击事件发生的位置。
另外,在给定链接中提供了一个工作示例和一个关于这个主题的详细文章,可以进一步了解如何在Angular中检测点击事件发生在组件之外。
这就是关于如何在Angular组件中检测点击事件发生在组件之外的原因和解决方法。