1

Starting with RxJS and want to create a simple stream of button clicks, so I simply do this:

export class AppComponent {

    button : HTMLElement = document.querySelector('button');

    refreshClickStream$ = Observable.fromEvent(this.button, 'click')
       .subscribe();

    constructor(){
    }  

but receive that error in console...

Also tried it like this:

  clicked(e) {
    return Observable.fromEvent(e.target, 'click')
    .do(console.log)
       .subscribe();
  }

But after first click I get no output in console. After second click I get one MouseEvent object. After third click I get two MouseEvent Objects. After fourth click I get three of them etc. So I think click event handler duplicates with RxJS fromEvent function.

Any explanations are very welcome..

Max Koretskyi
  • 101,079
  • 60
  • 333
  • 488
Julius Dzidzevičius
  • 10,775
  • 11
  • 36
  • 81
  • mm myabe have you tried to do it no in the clicked event ..but maybe in the init of the component? ..cause i think at every click you do a new subscription to the event – federico scamuzzi Aug 02 '17 at 15:28
  • Agree, I also thought that I would probably get many subscriptions. However I left this question for future, for the moment I cant get any streams of this, because of that error... – Julius Dzidzevičius Aug 02 '17 at 15:32

1 Answers1

2

You have the following setup:

AppComponent 
   template: '<button>some button</button>'
   button : HTMLElement = document.querySelector('button');

The top level code you put in the class will be used as a body of the constructor when transpiled to JavaScript:

class AppComponent {
   constructor() {
       let HTMLElement = document.querySelector('button');
   }
}

Now, Angular goes through the tree of components and creates a view with DOM nodes for each component one by one. So it first creates a view for the component that hosts app-component. It generates a DOM host element for it and then calls the AppComponent constructor. At this point the view for the AppComponent with button DOM element hasn't been created yet. So when the constructor is executed, you try to find the DOM element button which is not yet created.

To do you what you're trying to do you can use any of the lifecycle hooks. To get more information read my answer for the Difference between Constructor and ngOnInit.

Besides this problem, I would also advise to use @ViewChild for the DOM query and ngOnInit lifecycle hook like this:

template: `<button #b>...</button>`
...
class AppComponent {
   @ViewChild('b') button;

   ngOnInit() {
       this.refreshClickStream$ = Observable.fromEvent(this.button, 'click').subscribe();
   }
}
Max Koretskyi
  • 101,079
  • 60
  • 333
  • 488
  • Well now I am much smarter. Many thanks! Could you also write - 1. Why you suggest to use @ViewChild? 2. My app-component is the main component, so what is its host..? – Julius Dzidzevičius Aug 02 '17 at 17:50
  • 1
    Use view child for the platform independent code. Using it you don't have to use `querySelector` native browser API method. For every entry/root component Angular generates a wrapper host component. You don't see it, but it's there) You can see the factory `App_Component_Host` generated for it in the chrome debugging tools. – Max Koretskyi Aug 02 '17 at 17:54