首页 文章

动态添加事件侦听器

提问于
浏览
120

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

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

我有点使用普通的Javascript来抓取元素,然后调用标准 addEventListener() ,但我想知道是否有更多的“Angular2.0”方式这样做我应该调查 .

3 回答

  • 3

    Renderer已在Angular 4.0.0-rc.1中弃用,请阅读下面的更新

    angular2方式是使用 listenlistenGlobal 来自Renderer

    例如,如果要将Click事件添加到Component,则必须使用Renderer和ElementRef(这也为您提供了使用ViewChild的选项,或者任何检索 nativeElement 的选项)

    constructor(elementRef: ElementRef, renderer: Renderer) {
    
        // Listen to click events in the component
        renderer.listen(elementRef.nativeElement, 'click', (event) => {
          // Do something with 'event'
        })
    );
    

    您可以使用 listenGlobal 来访问 documentbody 等 .

    renderer.listenGlobal('document', 'click', (event) => {
      // Do something with 'event'
    });
    

    请注意,自beta.2起, listenlistenGlobal 都会返回一个删除侦听器的函数(请参阅更改日志中的breaking changes部分以获取beta.2) . 这是为了避免大型应用程序中的内存泄漏(请参阅#6686) .

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

    // 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();
    }
    

    这是一个plnkr的例子 . 该示例包含 listenlistenGlobal 的用法 .

    将RendererV2与Angular 4.0.0-rc.1一起使用(Renderer2自4.0.0-rc.3起)

    • 25/02/2017Renderer 已被弃用,现在我们应该使用RendererV2(见下面的行) . 见commit .

    • 10/03/2017RendererV2 已重命名为 Renderer2 . 见breaking changes .

    RendererV2没有更多 listenGlobal 函数用于全局事件(文档,正文,窗口) . 它只有 listen 功能,可实现两种功能 .

    作为参考,我复制并粘贴DOM Renderer实现的source code,因为它可能会改变(是的,它是有角度的!) .

    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;
      }
    

    如您所见,现在它验证我们是否传递字符串(文档,正文或窗口),在这种情况下它将使用内部 addGlobalEventListener 函数 . 在任何其他情况下,当我们传递一个元素(nativeElement)时,它将使用一个简单的 addEventListener

    要移除侦听器,它与角度2.x中的 Renderer 相同 . listen 返回一个函数,然后调用该函数 .

    示例

    // Add listeners
    let global = this.renderer.listen('document', 'click', (evt) => {
      console.log('Clicking the document', evt);
    })
    
    let simple = this.renderer.listen(this.myButton.nativeElement, 'click', (evt) => {
      console.log('Clicking the button', evt);
    });
    
    // Remove listeners
    global();
    simple();
    

    plnkrAngular 4.0.0-rc.1 使用 RendererV2

    plnkrAngular 4.0.0-rc.3 使用 Renderer2

  • 0

    我发现这非常令人困惑 . 正如@EricMartinez指出的那样Renderer2 listen()返回删除监听器的函数:

    ƒ () { return element.removeEventListener(eventName, /** @type {?} */ (handler), false); }
    

    如果我正在添加一个听众

    this.listenToClick = this.renderer.listen('document', 'click', (evt) => {
          alert('Clicking the document');
        })
    

    我希望我的函数执行我想要的,而不是删除监听器的完全相反 .

    // I´d expect an alert('Clicking the document'); 
    this.listenToClick();
    // what you actually get is removing the listener, so nothing...
    

    在给定的场景中,它实际上更有意义地命名它:

    // Add listeners
    let unlistenGlobal = this.renderer.listen('document', 'click', (evt) => {
      console.log('Clicking the document', evt);
    })
    
    let removeSimple = this.renderer.listen(this.myButton.nativeElement, 'click', (evt) => {
      console.log('Clicking the button', evt);
    });
    

    必须有一个很好的理由,但在我看来这是非常误导和不直观的 .

  • 223

    这是我的解决方法:

    我用Angular 6创建了一个库 . 我添加了一个通用组件 commonlib-header ,在外部应用程序中使用这个组件 .

    注意 serviceReference 是保存 stringFunctionName 方法的类(在使用 commonlib-header 的组件 constructor(public serviceReference: MyService) 中注入):

    <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>
    

相关问题