25

I am getting a weird error when trying to subscribe to an Observable.

Here is a watered down version of the code which presents the problem:

import {Component, Input, OnInit, ViewChild} from '@angular/core';
import Rx from 'rxjs/Rx';

@Component({
  selector: 'action-overview-description',
  template: require('./actionOverviewDescription.html')
})
export class ActionOverviewDescription  {
  @ViewChild('button') button;

  constructor() {}
  
   ngOnInit() {

    let buttonStream$ = Rx.Observable.fromEvent(this.button, 'click')
        .subscribe(res => console.log(res));

  }
}
<button #input>Button</button>

The error I get in the console is:

Invalid event target

The error ONLY shows up when I subscribe to the stream. If I only create it but don't subscribe, there is no error. If I console.log the stream it seems to have a subscribe member.

I have tried googling the error but I can't seem to find anywhere it's explained.

I think I'm using Rxjs 4.0.5 (according to npm rxjs --version).

Roman C
  • 49,761
  • 33
  • 66
  • 176
deafdigit
  • 303
  • 1
  • 3
  • 5

2 Answers2

62

The problem is the lifecycle hook you're using. The element is not yet creating in DOM when ngOnInit is called. Instead, you should use ngAfterViewInit.

Could you try the following code:

import { Component, ViewChild, ElementRef, AfterViewInit } from '@angular/core';
import { Observable, fromEvent } from 'rxjs';

@Component({
  template: '<button #input>Button</button>'
})
export class ActionOverviewDescription implements AfterViewInit {
  @ViewChild('input') button: ElementRef;

  ngAfterViewInit() {
    let buttonStream$ = Observable.fromEvent(this.button.nativeElement, 'click')
        .subscribe(res => console.log(res));

  }
}
George Oiko
  • 1,423
  • 2
  • 17
  • 24
AngularChef
  • 13,797
  • 8
  • 53
  • 69
  • Okay after fiddling around for a while it now works! I think the error may be in the way I imported the Rxjs-stuff. Maybe the missing "import 'rxjs/add/observable/fromEvent'. It's just a rather undescriptive error message if that's the case. I also was under the impression ElementRef was deprecated or something... in any event - it now works! Thank you to AngularFrance for the answer. – deafdigit Feb 25 '17 at 14:36
  • Ok, good. :) ElementRef is [marked as stable in the API](https://angular.io/docs/ts/latest/api/core/index/ElementRef-class.html), I don't think it's deprecated. Anyways it's just a type annotation. You could replace it with `any` and the code would work just the same. FYI, I think the main error in your code was targeting `this.button` vs `this.button.nativeElement`. – AngularChef Feb 25 '17 at 14:41
  • 6
    This would not work for me. I ended up defining button as type : any, then used Observable.fromEvent(this.button._elementRef.nativeElement , 'click') - I was using within the context of an Ionic 3 project. It uses Angular 4 under the covers/.. – JGFMK Sep 02 '17 at 15:50
  • 1
    That's because your button was probably of type `MatButton`, @JGFMK, or something similar, not `ElementRef` – Paul Melero Jan 30 '18 at 13:13
  • 1
    Btw, ViewChild will always be available in ngOnInit(). You only need afterViewInit for Viewchildren, and a ViewChild that's in *ngFor, ngIf or ng-template. In the source code of Angular ViewChild is considered "static" and has to be available before ngOnInit is called (search for "ViewChild static ngOnInit") https://github.com/angular/angular/issues/21800#issuecomment-360799520 – Drenai May 21 '18 at 18:18
  • Just to elaborate on Drenai's comment: `@ViewChild('input', {'static': true}) button: ElementRef;` would ensure it to be static. – Tsan-Kuang Lee Aug 01 '19 at 23:16
1

If you want to access it in ngOnInit event then you would have to use { static: true } property of ViewChild something like this:

import { Component, ViewChild, ElementRef, OnInit } from '@angular/core';
import { Observable, fromEvent } from 'rxjs';

@Component({
  template: '<button #input>Button</button>'
})
export class ActionOverviewDescription implements OnInit {
  @ViewChild('input', { static: true }) button: ElementRef;

  ngOnInit() {
    let buttonStream$ = Observable.fromEvent(this.button.nativeElement, 'click')
        .subscribe(res => console.log(res));

  }
}
Jameer Khan
  • 373
  • 2
  • 10