Angular 和动态添加的 CKEditors 中断

Angular and dynamically added CKEditors break

我在通过 NgForangular@2.0.0-alpha.44 动态添加的 CKEditors 渲染时遇到问题。

Live demo is available here.

@Directive({
    selector: 'textarea'
})
class CKEditor {
    constructor(_elm: ElementRef) {
        CKEDITOR.replace(_elm.nativeElement);
    }
}

@Component({
    selector: 'my-app',
})
@View({
    directives: [NgFor, CKEditor],
    template: `
      <div *ng-for="#item of items">
        {{ item }}: <textarea>{{ item }}</textarea>
      </div>
      <button (click)="addItem()">Add</button>`
})
class AppComponent {
    items = ['default_0', 'default_1'];

    constructor() {
        this.addItem();
    }

    addItem() {
        var id = 'ckeditor_inst_' + this.items.length;
        this.items.push(id);
    }
}

您可以看到三个正常工作的 CKEditor。然后单击底部的 "Add" 按钮,它会破坏容器中的最后一个 CKEditor,您甚至可以写入它,如果您按任何工具栏按钮,它会抛出:

Uncaught TypeError: Cannot read property 'getSelection' of undefined.

有趣的是,只有最后一个 CKEditor 坏了,其他两个都可以。似乎 Angular2 以某种方式操纵了最后一个破坏 CKEditor 的元素。

我记得在 angular@2.0.0-alpha.35 中使用相同的方式添加新的 CKEditor,我认为它在那里有效,但也许我只是没有注意到。版本 angular@2.0.0-alpha.47 is the same.

您的集成使用 Classic CKEditor,因为 wysiwygarea 插件允许在 <iframe> 中进行编辑(即避免 CSS 与网页冲突)。

这种实现的缺点是,一旦您从 DOM 中分离(并重新附加)这样的 <iframe>(就像 Angular 每次添加新的 item),其内部 document 得到 "broken"。我所说的损坏是指 document.body 从头开始​​加载,丢失您的内容、CKEditor bootstrap 代码、JS 引用等,并最终使整个编辑器实例无用。

所以问题出在这个视图的呈现方式上:

@View({
    directives: [NgFor, CKEditor],
    template: `
      <div *ng-for="#item of items">
        {{ item }}: <textarea>{{ item }}</textarea>
      </div>
      <button (click)="addItem()">Add</button>`
})

我看到了这个问题的三个解决方案:

  • 清洁解决方案:强制 Angular 在添加新项目时不重新渲染整个 items 集合。
  • 棘手的解决方案:使用 Inline CKEditor,它在 <div contenteditable="true" /> div 而不是 <iframe>...<body contenteditable="true" /></iframe> 中工作,并且不受 DOM 中的突变影响。
  • 懒惰且缓慢的解决方案:坚持当前集成,但在添加新项目之前销毁所有 CKEDITOR.instances (instance.destroy()),然后重新初始化它们 CKEDITOR.replace().

此问题已在 beta-15 中修复,请参阅:

http://plnkr.co/edit/X5whPqDhLS6RjTR2N8hT?p=preview