我刚刚开始使用Angular 2,我想知道是否有人能告诉我从元素中动态添加和删除事件监听器的最佳方法。
我有一个组件设置。当单击模板中的某个元素时,我想将mousemove
的侦听器添加到同一模板的另一个元素。然后,我想在单击第三个元素时删除此侦听器。
我有点工作只是使用普通的Javascript来抓取元素,然后调用标准addEventListener()
,但我想知道是否还有更多" Angular2.0 & #34;这样做的方式,我应该调查。
答案 0 :(得分:247)
使用Renderer 例如,如果要将Click事件添加到Component,则必须使用Renderer和ElementRef(这也为您提供了使用ViewChild的选项,或者任何检索listen
的内容)
listenGlobal
您可以使用nativeElement
来访问constructor(elementRef: ElementRef, renderer: Renderer) {
// Listen to click events in the component
renderer.listen(elementRef.nativeElement, 'click', (event) => {
// Do something with 'event'
})
);
,listenGlobal
等。
document
请注意,自beta.2以来,body
和renderer.listenGlobal('document', 'click', (event) => {
// Do something with 'event'
});
都会返回一个删除侦听器的函数(请参阅更新日志中的breaking changes部分for beta.2)。这是为了避免大型应用程序中的内存泄漏(请参阅#6686)。
因此,为了删除我们动态添加的侦听器,我们必须将listen
或listenGlobal
分配给将保存返回函数的变量,然后执行它。
listen
这是一个plnkr示例工作。该示例包含listenGlobal
和// listenFunc will hold the function returned by "renderer.listen"
listenFunc: Function;
// globalListenFunc will hold the function returned by "renderer.listenGlobal"
globalListenFunc: Function;
constructor(elementRef: ElementRef, renderer: Renderer) {
// We cache the function "listen" returns
this.listenFunc = renderer.listen(elementRef.nativeElement, 'click', (event) => {
// Do something with 'event'
});
// We cache the function "listenGlobal" returns
this.globalListenFunc = renderer.listenGlobal('document', 'click', (event) => {
// Do something with 'event'
});
}
ngOnDestroy() {
// We execute both functions to remove the respectives listeners
// Removes "listen" listener
this.listenFunc();
// Removs "listenGlobal" listener
this.globalListenFunc();
}
的使用情况。
2017年2月25日:listen
已被弃用,现在我们应该使用 (参见下面的行)。请参阅commit。listenGlobal
10/03/2017 :Renderer
已重命名为RendererV2
。请参阅breaking changes。
RendererV2
对全局事件(文档,正文,窗口)不再有RendererV2
函数。它只有一个Renderer2
函数,可以实现两种功能。
供参考,我是副本&粘贴DOM渲染器实现的source code,因为它可能会改变(是的,它是有角度的!)。
listenGlobal
如您所见,现在它验证我们是否传递字符串(文档,正文或窗口),在这种情况下它将使用内部listen
函数。在任何其他情况下,当我们传递一个元素(nativeElement)时,它将使用一个简单的listen(target: 'window'|'document'|'body'|any, event: string, callback: (event: any) => boolean):
() => void {
if (typeof target === 'string') {
return <() => void>this.eventManager.addGlobalEventListener(
target, event, decoratePreventDefault(callback));
}
return <() => void>this.eventManager.addEventListener(
target, event, decoratePreventDefault(callback)) as() => void;
}
要移除侦听器,它与角度2.x中的addGlobalEventListener
相同。 addEventListener
返回一个函数,然后调用该函数。
Renderer
plnkr Angular 4.0.0-rc.1 ,使用 RendererV2
plnkr Angular 4.0.0-rc.3 ,使用 Renderer2
答案 1 :(得分:0)
这是我的解决方法:
我使用Angular 6创建了一个库。我添加了一个公共组件commonlib-header
,该组件在外部应用程序中就这样使用。
请注意serviceReference
,它是持有constructor(public serviceReference: MyService)
方法的类(注入到使用commonlib-header
的组件stringFunctionName
中):
<commonlib-header
[logo]="{ src: 'assets/img/logo.svg', alt: 'Logo', href: '#' }"
[buttons]="[{ index: 0, innerHtml: 'Button', class: 'btn btn-primary', onClick: [serviceReference, 'stringFunctionName', ['arg1','arg2','arg3']] }]">
</common-header>
库组件的编程如下。动态事件已添加到onClick(fn: any)
方法中:
export class HeaderComponent implements OnInit {
_buttons: Array<NavItem> = []
@Input()
set buttons(buttons: Array<any>) {
buttons.forEach(navItem => {
let _navItem = new NavItem(navItem.href, navItem.innerHtml)
_navItem.class = navItem.class
_navItem.onClick = navItem.onClick // this is the array from the component @Input properties above
this._buttons[navItem.index] = _navItem
})
}
constructor() {}
ngOnInit() {}
onClick(fn: any){
let ref = fn[0]
let fnName = fn[1]
let args = fn[2]
ref[fnName].apply(ref, args)
}
可重复使用的header.component.html
:
<div class="topbar-right">
<button *ngFor="let btn of _buttons"
class="{{ btn.class }}"
(click)="onClick(btn.onClick)"
[innerHTML]="btn.innerHtml | keepHtml"></button>
</div>
答案 2 :(得分:0)
我将添加一个 StackBlitz example 和来自@tahiche的答案的注释。
返回值是在添加事件侦听器后将其删除的函数。在不再需要事件侦听器时,将其视为一种好习惯。因此,您可以存储此返回值并在ngOnDestroy
方法内调用它。
我承认乍一看似乎令人困惑,但这实际上是一个非常有用的功能。自己之后还能如何清理?
export class MyComponent implements OnInit, OnDestroy {
public removeEventListener: () => void;
constructor(
private renderer: Renderer2,
private elementRef: ElementRef
) {
}
public ngOnInit() {
this.removeEventListener = this.renderer.listen(this.elementRef.nativeElement, 'click', (event) => {
if (event.target instanceof HTMLAnchorElement) {
// Prevent opening anchors the default way
event.preventDefault();
// Your custom anchor click event handler
this.handleAnchorClick(event);
}
});
}
public ngOnDestroy() {
this.removeEventListener();
}
}
您可以找到 a StackBlitz here ,以说明如何在捕获锚元素上捕获点击。
我添加了一个带有图像的物体,如下所示:
<img src="x" onerror="alert(1)"></div>
表示消毒剂正在发挥作用。
Here in this fiddle,您发现相同的主体附着在innerHTML
上而没有对其进行消毒,它将证明问题所在。