动态添加事件侦听器

时间:2016-01-29 08:53:55

标签: angular

我刚刚开始使用Angular 2,我想知道是否有人能告诉我从元素中动态添加和删除事件监听器的最佳方法。

我有一个组件设置。当单击模板中的某个元素时,我想将mousemove的侦听器添加到同一模板的另一个元素。然后,我想在单击第三个元素时删除此侦听器。

我有点工作只是使用普通的Javascript来抓取元素,然后调用标准addEventListener(),但我想知道是否还有更多" Angular2.0 & #34;这样做的方式,我应该调查。

3 个答案:

答案 0 :(得分:247)

Renalrer已在Angular 4.0.0-rc.1中弃用,请阅读以下更新

使用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以来,bodyrenderer.listenGlobal('document', 'click', (event) => { // Do something with 'event' }); 都会返回一个删除侦听器的函数(请参阅更新日志中的breaking changes部分for beta.2)。这是为了避免大型应用程序中的内存泄漏(请参阅#6686)。

因此,为了删除我们动态添加的侦听器,我们必须将listenlistenGlobal分配给将保存返回函数的变量,然后执行它。

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(); } 的使用情况。

使用RendererV2和Angular 4.0.0-rc.1 + (Renderer2自4.0.0-rc.3)

  • 2017年2月25日listen已被弃用,现在我们应该使用 listenGlobal (参见下面的行)。请参阅commit

  • 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上而没有对其进行消毒,它将证明问题所在。