Angular 和动态添加的 CKEditors 中断
Angular and dynamically added CKEditors break
我在通过 NgFor
和 angular@2.0.0-alpha.44
动态添加的 CKEditors 渲染时遇到问题。
@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
中修复,请参阅:
我在通过 NgFor
和 angular@2.0.0-alpha.44
动态添加的 CKEditors 渲染时遇到问题。
@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
中修复,请参阅: