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()
.