ckeditor/ckeditor5-angular

Performance issue while rendering multiple editors in `ngFor` loop

Mgsy opened this issue · 3 comments

Mgsy commented

Steps to reproduce

  1. Clone the example project - https://github.com/raziahmadansari/editor-demo
  2. Run npm i && ng serve
  3. Visit the sample
  4. Click "Add editor" button a few times

Rendering method:

<ng-container *ngFor="let editor of editors; index as editorId">
  <app-editor [editorInstanceId]="editorId"></app-editor>
</ng-container>

Current result

Each time the editor is added, the browser starts responding slower, until it freezes.

Notes

  • The same amount of editors rendered at once doesn't cause any issues

To be checked:

  • Is this ngFor use correct? What about trackBy e.g.? This may help already.
  • Isn't this made worse by a memory leak. Check e.g. for console.logs( editor ) because it gets the editor instances retained forever.
Mgsy commented

The issue is caused by the fact that all editors re-render after adding a new editor. The solution is to make sure that only one component is added, without triggering rendering of all of them.

Mgsy commented

Full solution: https://github.com/raziahmadansari/editor-demo/tree/main/src/app/loop-demo

The example mechanism for rendering components with trackBy :

loop-demo.component.html

<h1>CKEditor Demo</h1>
<ng-container
  *ngFor="let editor of editors; index as editorId; trackBy: trackEditor"
>
  <div class="mb-10">
    <app-editor
      class="mb-10"
      [editorInstanceId]="editorId"
      (onEditorReady)="onReady($event)"
    ></app-editor>
  </div>
</ng-container>
<button type="button" (click)="addEditor()">+ Add Editor</button>
<button type="button" (click)="removeEditors()" class="ml-10">
  X Remove Editors
</button>

loop-demo.component.ts

import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-loop-demo',
  templateUrl: './loop-demo.component.html',
  styleUrl: './loop-demo.component.scss',
})
export class LoopDemoComponent implements OnInit {
  editors: Array<any> = [];

  ngOnInit(): void {
    // this.loadEditors(4);
  }

  loadEditors(editorCount: number): void {
    for (let editorId = 0; editorId < editorCount; editorId++) {
      this.editors.push(editorId);
    }
  }

  addEditor(): void {
    this.editors.push(this.editors.length);
  }

  removeEditors(): void {
    this.editors = [];
  }

  onReady(editor: any): void {
    // this.editors[this.editors.length - 1] = editor;
  }

  trackEditor(index: number, item: any): number {
    return item;
  }
}