3

I have already tried multiple variations also following This Possible Dup

Using the ng2-codemirror 1.1.3 lib + codemirror 5.33.0 anchor

All I'm trying to do is to attach a DebounceTime operator to the change event of the CodeMirror Editor

html:

<codemirror #cm [(ngModel)]="code" [config]="config" (focus)="onFocus()" (blur)="onBlur()"></codemirror>

ts:

import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/fromEvent';
import 'rxjs/add/operator/debounceTime';

@ViewChild('cm') editor;

ngAfterViewInit() {
  const watch = Observable.fromEvent(this.editor, 'change'); // <--- Error
  watch.subscribe(v => console.log(v));
}

The error I'm getting is:

ERROR TypeError: Invalid event target

I've also tried attaching the Observable.fromEvent to this.editor.value/ this.editor.input

EDIT Entire Component: component.HTML:

<codemirror #cm [(ngModel)]="code" [config]="config" (focus)="onFocus()" (blur)="onBlur()"></codemirror>

component.TS:

import { AfterViewInit, Component, ElementRef, OnInit, ViewChild } from '@angular/core';
import { global } from '../shared/global.constants';
import 'codemirror/mode/javascript/javascript';
import 'codemirror/addon/scroll/simplescrollbars';
import 'codemirror/addon/hint/javascript-hint';
import 'codemirror/addon/hint/show-hint.js';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/fromEvent';
import 'rxjs/add/operator/debounceTime';

@Component({
  selector: 'app-main',
  templateUrl: './main.component.html',
  styleUrls: ['./main.component.scss']
})
export class MainComponent implements OnInit, AfterViewInit {
  @ViewChild('cm') editor;
  @ViewChild('output') output;
  code = global.code;
  config = {
    lineNumbers: true,
    mode: {name: 'javascript', json: true},
    tabSize: 2,
    scrollbarStyle: 'simple',
    extraKeys: {'Tab': 'autocomplete', 'Ctrl-Space': 'autocomplete'}
  };

  constructor() {

  }

  ngOnInit() {

  }

  ngAfterViewInit() {
    console.log(this.editor); // <--- CodemirrorComponent {change: EventEmitter, focus: EventEmitter, blur: EventEmitter, cursorActivity: EventEmitter, instance: CodeMirror$1, …}
    console.log(this.editor.nativeElement); // <--- undefined
    const watch = Observable.fromEvent(this.editor.host.nativeElement, 'input');
    console.log(watch);
    watch.subscribe(w => console.log(w)); // <-- invalid target
  }
}
Estus Flask
  • 206,104
  • 70
  • 425
  • 565
shuk
  • 1,745
  • 1
  • 14
  • 25

2 Answers2

3

Passon element (nativeElement) to Observable.fromEvent method.

ngAfterViewInit() {
  const watch = Observable.fromEvent(this.editor.nativeElement, 'change');
  watch.subscribe(v => console.log(v));
}
Pankaj Parkar
  • 134,766
  • 23
  • 234
  • 299
  • Hey. Tried, still same error. even went ahead and added `@ViewChild('cm') editor: ElementRef;` as shows in the link I attached – shuk Jan 31 '18 at 06:23
  • @estus. I understand :) Please see the edit - I added logs (`this.editor.nativeElement` returns `undefined`) – shuk Jan 31 '18 at 07:04
  • @OmerShukar It says nothing on what `this.editor` equals to. The question doesn't contain the entire component, only snippets, so it's not obvious that `#cm` is really in component template. There may be a mistake on your side, that's why http://stackoverflow.com/help/mcve is essential in trouble-solving questions. – Estus Flask Jan 31 '18 at 07:30
  • @estus Added entire component – shuk Jan 31 '18 at 07:40
  • @OmerShukar does `#cm` / `codemirror` belongs to `main.component.html`?? – Pankaj Parkar Jan 31 '18 at 07:55
  • @PankajParkar Yes! – shuk Jan 31 '18 at 07:59
3

Considering that <codemirror> is a component, @ViewChild('cm') editor queries an instance of component class, while @ViewChild('cm') editor: ElementRef just tricks typing system but doesn't affect editor value.

As the reference explains, read property is used to specify which token should be queried. In order to force it to be ElementRef, it should be:

@ViewChild('cm', { read: ElementRef }) editor: ElementRef;

Then event listener can be added to DOM element (as another answer already explains):

const watch = Observable.fromEvent(this.editor.nativeElement, 'change');
Estus Flask
  • 206,104
  • 70
  • 425
  • 565
  • `const watch = Observable.fromEvent(this.editor.nativeElement, 'input'); watch.debounceTime(200).subscribe(e => { console.log(this.editor.nativeElement.innerText); });` Works - fantastic!! thank you and @PankajParkar so much!!! – shuk Jan 31 '18 at 08:34
  • This is an interesting. I'll investigate more on this, thanks @estus for pointing this out. – Pankaj Parkar Jan 31 '18 at 09:21
  • 1
    You're welcome. @PankajParkar Sure. IIRC, by default string query (template variable name) returns ElementRef for regular elements and directives but class instance for components, so it's a good practice to use explicit `read` whenever possible. – Estus Flask Jan 31 '18 at 14:46
  • Does this have solution for vanilla js? I am not from Angular, I am not able to grasp it. Can someone explain? – Rehan Jun 02 '20 at 15:43
  • @Rehan Both the question and the answer are very specific to ng2-codemirror and not CodeMirror in general. RxJS Observable is not exclusive to Angular but is used here because they work well together, you may not need it for vanilla JS. I'd suggest to ask a new question that explains your problem in detail. – Estus Flask Jun 02 '20 at 16:20
  • Sure, I have opened up a question regarding. https://stackoverflow.com/questions/62157612/debouncing-codemirror-input-on-change-event Can you check? Thanks @EstusFlask – Rehan Jun 02 '20 at 17:17