1

I've run into big performance problem when I tried to implement codemirror, firstly I used ng2-codemirror but it's killed my application which has only list of scripts and codemirror component. I found out that creating instance (by instance i mean CodeMirror.fromTextArea(...)) should be run outside angular so I copied component from ng2-codemirror (code attached below) to my project and wrap init method with NgZone.runOutsideAngular, it helps but just a bit. So I've started profiling my angular project with Javascript profiler in chrome debugger and vanilla js codemirror with the same code to edit. My small research shows me that without zone.js codemirror is executing same function with about 8% of my CPU resources however with zone.js in angular project it takes 48%. So I've started to completly detach zone from codemirror component, So I tried with ChangeDetectionRef.detach() but the zone still is attached to codemirror (checked with JS profiler again).

Summary: runOutsideAngular does not help to detach codemirror source code from zone, same is for detaching changeDetector for codemirror component

Code for my modified codemirror component

export class MpCodemirrorComponent implements AfterViewInit, OnDestroy {

  @Input() config;
  @Output() change = new EventEmitter();
  @Output() focus = new EventEmitter();
  @Output() blur = new EventEmitter();
  @Output() cursorActivity = new EventEmitter();

  @ViewChild('host') host;

  @Output() instance = null;

  _value = '';

  private changeSubject: Subject<any> = new Subject<any>();

  constructor( 
    private ngZone: NgZone,
    private changeDetector: ChangeDetectorRef
  ) {
    this.changeDetector.detach();
  }

  get value() { return this._value; }

  @Input() set value(v) {
    if (v !== this._value) {
      this._value = v;
      this.onChange(v);
    }
  }

  ngOnDestroy() {

  }

  ngAfterViewInit() {
    this.changeDetector.detach();

    this.config = this.config || {};
    this.codemirrorInit(this.config);
    this.changeSubject
      .debounceTime(500)
      .distinctUntilChanged()
      .subscribe(change => this.updateValue(change));
  }

  codemirrorInit(config) {
    this.ngZone.runOutsideAngular(() => {
      this.instance = CodeMirror.fromTextArea(this.host.nativeElement, config);
      this.instance.setValue(this._value);
      this.instance.on('change', () => {
        this.changeSubject.next(this.instance.getValue());
        // this.updateValue(this.instance.getValue());
      });

      this.instance.on('focus', (instance, event) => {
        this.focus.emit({instance, event});
      });

      this.instance.on('cursorActivity', (instance) => {
        this.cursorActivity.emit({instance});
      });

      this.instance.on('blur', (instance, event) => {
        this.blur.emit({instance, event});
      });
    })

  }

  updateValue(value) {
    this.value = value;
    this.onTouched();
    this.change.emit(value);
  }

  writeValue(value) {
    this._value = value || '';
    if (this.instance) {
      this.instance.setValue(this._value);
    }
  }
  onChange(_) {}
  onTouched() {}
  registerOnChange(fn) { this.onChange = fn; }
  registerOnTouched(fn) { this.onTouched = fn; }
}

I've run out of ideas how to completly detach zone for 3rd party library, any help is appreciated.

P.S. Don't mind doubled changeDetector.detach().

Patryk Brejdak
  • 1,571
  • 14
  • 26

0 Answers0