在Angular2中,从<button>点击事件创建可观察对象

49

用Angular 2从按钮的onclick事件创建observable的首选方式是什么?

我不确定在组件代码中从DOM获取原始元素是否被认为是最佳实践(如何做到这一点?),或者是否有其他我不知道的捷径。


2
请参见https://github.com/angular/angular/issues/4062。 - Mark Rajcok
@MarkRajcok,谢谢你的启发。现在有什么好的方法来做这件事吗? - GBa
5个回答

49

不要想太多。

@ViewChild('button') button;
clicks$:Observable<any>;

ngOnInit() {
  this.clicks$ = Observable.fromEvent(this.button.nativeElement, 'click');
}

3
注意:如果按钮由于 *ngIf 而一开始被隐藏,则按钮将为 null。虽然有办法解决这个问题(通过使用 button 的 setter 和 flatmap/switchmap),但它很快会变得复杂。在这种情况下最好使用 Jon 的答案。 - Simon_Weaver
2
我认为应该在ngAfterViewInit回调中调用fromEvent,因为在ngOnInit中,this.button仍然未定义... - user2010955
1
从 RxJS 6 开始,Observable.fromEvent() 已被弃用。请改用 fromEvent(),而不是 Observable.fromEvent() - Cichy
错误:无法读取未定义的属性(读取“nativeElement”) - sam sergiy klok

40

在阅读了rxjs的创作者文章后,我不同意第二种方法。 - Yew Hong Tat

16

由于我的编译器无法识别publ,所以@Gunter的示例对我没有起作用。

这是一个对我有用的示例:modal.component.ts

import { Output, Component } from '@angular/core';
import {Subject} from "rxjs/Subject";

export class MyModal{

    private clickStream = new Subject<Event>();

    @Output() observ = this.clickStream.asObservable();

    buttonClick(event:Event){
        this.clickStream.next(event);
    }
}

modal.component.html中:

<button type="button" class="btn btn-default" (click)="buttonClick($event)">click me</button>

6

如果您尝试使用@ViewChild,而按钮在初始化时(由于*ngIf)不可见,则赋值将为空。

您可以与@ViewChild一起使用setter,在按钮首次出现时运行初始化。

@ViewChild('btnAdd')
set btnAdd(btnAdd: Button) { ... } 

这种方法很快就会变得笨拙和不方便 - 特别是如果你从中创建了一个可观察的流。

一种混合方式可能如下:

btnAskAnotherClicks$ = new Subject<Event>();

<button mat-flat-button (click)="btnAskAnotherClicks$.next($event)">Ask another question...</button>

这使您可以使用点击流创建链式结构,但如果由于 *ngIf 而按钮最初处于隐藏状态,则不会出现任何问题。
不喜欢在模板中使用 next?我也不是特别喜欢。但是我可以接受 async,它们都是实现细节。好吧,这取决于您的决定 -)

事实证明,您也可以使用 new Subject<void>() 然后进行 btn.next() 而无需传递值。 如果事件没有有用的含义,这可能很有用。 - Simon_Weaver
目前在主题的管道中使用exhaustMap来阻止垃圾邮件http调用。但是在一个调用完成后,“垃圾邮件发送者”仍然可以继续进行。有没有解决这个问题的方法? - liqSTAR

3

如果您正在使用AngularMaterial按钮和可管道化的RxJS操作符,则需要对@JoshuaDavid的答案进行一些轻微修改:

在模板中标记了某些按钮的模板变量:

<button #btnTemplateName mat-icon-button></button>

组件代码如下:
import { Observable, fromEvent } from 'rxjs';

// Note importing from lettable/pipeable operators - 'operators' plural
import { tap } from 'rxjs/operators';

import { MatButton } from '@angular/material/button';

//Access the button through the template variable, typed to MatButton
@ViewChild('btnTemplateName') myBtn: MatButton;
myBtnClicks$: Observable<any>;


ngAfterViewInit() {

    // Note the need to access the native element in MatButton through the extended property chain
    this.myBtnClicks$ = 
      Observable.fromEvent(this.myBtn._elementRef.nativeElement, 'click');

    // Can now subscribe (using lettable/pipeable operators)
    this.myBtnClicks$.pipe(
       tap(() => console.log("Button clicked")),
    )
    .subscribe(event => console.log("Event:" + JSON.stringify(event)));
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接