ckeditor / ckeditor5-angular

Official CKEditor 5 Angular 5+ component.
https://ckeditor.com/ckeditor-5
Other
202 stars 110 forks source link

Performance issue while rendering multiple editors in `ngFor` loop #417

Closed Mgsy closed 3 months ago

Mgsy commented 3 months ago

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

Reinmar commented 3 months ago

To be checked:

Mgsy commented 3 months ago

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 2 months ago

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;
  }
}