【ExtJs】Ext.form.CKEditor:整合CKEditor的组件
CKEditor是一个出色的所见即所得的富文本编辑器,相比ExtJs原生的htmleditor要强大很多,于是把CKEditor集成到ExtJs中便是一个很好的选择。
从ExtJs官方论坛找到了整合的实例,不过其中的问题不少,比如组件setValue、isDiry等方法无法得到正确的结果。经过改造,修正了以上问题,并将CKFinder做了整合,同时增加了对CKEditor工具栏选择配置的支持。
修改后的Ext.form.CKEditor组件源码如下:
/**************************************************** * CKEditor Extension * by Maple Nan 2010.7.21 * http://witmax.cn/extjs-ckeditor.html *****************************************************/ Ext.form.CKEditor = function(config){ this.config = config; Ext.form.CKEditor.superclass.constructor.call(this, config); }; Ext.form.CKEditor.CKEDITOR_CONFIG = "/ckeditor/config.js"; Ext.form.CKEditor.CKEDITOR_TOOLBAR = "Default"; Ext.extend(Ext.form.CKEditor, Ext.form.TextArea, { onRender : function(ct, position){ if(!this.el){ this.defaultAutoCreate = { tag: "textarea", autocomplete: "off" }; } Ext.form.TextArea.superclass.onRender.call(this, ct, position); var config = { customConfig: Ext.form.CKEditor.CKEDITOR_CONFIG //,toolbar: Ext.form.CKEditor.CKEDITOR_TOOLBAR // 如需要默认工具条设置,请去掉行前注释 }; Ext.apply(config, this.config.CKConfig); var editor = CKEDITOR.replace(this.id, config); CKFinder.setupCKEditor( editor, './ckfinder/' ) ; }, onDestroy: function(){ if (CKEDITOR.instances[this.id]) { delete CKEDITOR.instances[this.id]; } }, setValue : function(value){ Ext.form.TextArea.superclass.setValue.apply(this,[value]); CKEDITOR.instances[this.id].setData( value ); }, getValue : function(){ CKEDITOR.instances[this.id].updateElement(); var value=CKEDITOR.instances[this.id].getData(); Ext.form.TextArea.superclass.setValue.apply(this,[value]); return Ext.form.TextArea.superclass.getValue.apply(this); }, getRawValue : function(){ CKEDITOR.instances[this.id].updateElement(); return Ext.form.TextArea.superclass.getRawValue(this); }, isDirty : function() { if(this.disabled) { return false; } var value = String(this.getValue()).replace(/\s/g,''); value = (value == "<br />" || value == "<br/>" ? "" : value); this.originalValue = this.originalValue || ""; this.originalValue = this.originalValue.replace(/\s/g,''); return String(value) !== String(this.originalValue) ? String(value) !== "<p>"+String(this.originalValue)+"</p>" : false; } }); Ext.reg('ckeditor', Ext.form.CKEditor);
由于Ext.form.CKEditor组件的特殊性,需要重写Ext.form.BasicForm组件的一些方法,对Ext.form.CKEditor组件提供特殊的支持,Ext.form.BasicForm组件重写部分源码如下:
// Add methods to the BasicForm that clears the isDirty flag to return "false" again. Ext.override(Ext.form.BasicForm,{ /** * clear the value of all items in BasicForm and set originalValue to '' * @param {Object} o */ clearValues: function(o){ o = o || this; o.items.each(function(f){ if(f.items){ this.clearValues(f); } else if(f.setValue){ f.setValue(''); // ckeditor needs being treated specially, or an error will appear in IE if (f.getXType() == "ckeditor"){ f.originalValue = ''; } else if (f.getValue){ f.originalValue = f.getValue(); } } }, this); this.clearInvalid(); } /** * clear isDirty flag of all items of BasicForm * @param {Object} o * * reference: http://www.extjs.com/forum/showthread.php?t=40568 */ ,clearDirty : function(o){ o = o || this; o.items.each(function(f){ if(f.items){ this.clearDirty(f); } else if(typeof(f.originalValue) != "undefined" && f.getValue){ // Ext.isEmpty(f.originalValue) && f.originalValue = f.getValue(); } }, this); } ,setValues : function(values){ if(Ext.isArray(values)){ // array of objects for(var i = 0, len = values.length; i < len; i++){ var v = values[i]; var f = this.findField(v.id); if(f){ f.setValue(v.value); // ckeditor is special if (f.getXType() == "ckeditor"){ f.originalValue = v.value; } // checkboxgroup.originalValue won't be set, or it may cause a bug when reset else if(this.trackResetOnLoad && typeof(f.originalValue) != "undefined" && f.getValue){ f.originalValue = f.getValue(); } } } }else{ // object hash var field, id; for(id in values){ if(typeof values[id] != 'function' && (field = this.findField(id))){ field.setValue(values[id]); if (this.trackResetOnLoad){ if (field.getXType() == "ckeditor"){ field.originalValue = values[id]; } else if(typeof(field.originalValue) != "undefined" && field.getValue){ field.originalValue = field.getValue(); } } } } } return this; } ,findField : function(id){ var field = this.items.get(id); if(!field){ this.items.each(function(f){ if(f.isXType('radiogroup')||f.isXType('checkboxgroup')){ if (f.isXType('radiogroup')) f.unitedValue = true; f.items.each(function(c){ if(c.isFormField && (c.dataIndex == id || c.id == id || c.getName() == id)){ field = f.unitedValue ? f : c; if (typeof(f.trackResetOnLoad) == "undefined") f.trackResetOnLoad = this.trackResetOnLoad; return false; } }, this); } if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){ field = f; return false; } }, this); } return field || null; } });
组件使用方法:
{ xtype: 'ckeditor', fieldLabel: 'Editor', name: 'htmlcode', CKConfig: { /* Enter your CKEditor config paramaters here or define a custom CKEditor config file. */ customConfig : '/ckeditor/config.js', toolbar: 'Other', height : 200, width: 250 } }
说明:以上customConfig为CKEditor组件配置文件的绝对路径(使用相对路径好像有问题,会无效,暂不清楚原因),不设定时默认为/ckeditor/config.js,默认值设定可以通过对Ext.form.CKEditor.CKEDITOR_CONFIG重新赋值来完成,也可修改Ext.form.CKEditor组件源码的对应部分;toolbar为采用的CKEditor配置文件中工具栏配置的名词,对于CKEditor工具栏如何配置可参考FckEditor(CKEditor)配置,不设定时默认为Default,此项默认值可以通过重新赋值Ext.form.CKEditor.CKEDITOR_TOOLBAR来设定或修改Ext.form.CKEditor组件源码的对应部分;width、height对应为组件显示的宽和高。
完整实例源码下载[download id="40" format="3"],其中ckeditor、ckfinder及extjs都需位于站点根目录下,具体路径也可自行修改实例源码。
这个问题怎么回事呢
TypeError: CKEDITOR.instances[this.id] is undefined
[回复]
晴枫 9月 21st, 2012 下午11:52 回复:
@xyc717, 请看一下上面的评论或许能帮你解决问题
[回复]
为什么我在用ie看效果的时候提示this.id不是对象或者对象为空
[回复]
晴枫 6月 19th, 2012 下午3:45 回复:
@郁闷, 看看上面的讨论有没有能帮你解决问题
[回复]
还有忘记提醒了:不能用FORM.LOAD这种方式加载赋值,因为它是自动赋值的,只能通过侦听事件的方式,用Ext.Ajax.request来加载服务器数据,然后逐一赋值.
[回复]
终于解决了,要在代码里,把每个TAB激活后,在赋值
核心代码:
Ext.getCmp(“EditBaseinfotab”).setActiveTab(‘EditBaseinfotab4’);
Ext.getCmp(“wincomment”).setValue(fdata[“wincomment”]);
‘EditBaseinfotab4’ 就是包含有ckeditor的TAB的 id, 希望对各位有帮助,在非window里加载TABPANEL 中的CKEDITOR 要激活选项才会被渲染的,设置deferredRender: false, 也没有用的,默认只会激活第一个TAB, 但是在window的TABPANEL 只要加了设置deferredRender: false, 所有的TAB项都被激活渲染的,到目前我也不明白原因。可能是Ext.form.CKEditor组件的问题,在渲染的时候要处理一下。
[回复]
同过WINDOW加载的TABPANEL 中的CKEDITOR 居然没有问题,其他方式的不行
[回复]
在TABPANEL 中的CKEDITOR 要激活选项才会被渲染的,郁闷,设置看deferredRender: false, 也没有用
[回复]
我也考虑过这个问题,通过按钮手工加载也不行,我在试试
[回复]
新问题:在TABLPANEL的FROM中的编辑器,好像初始化有问题,也是加载数据的时候:
ext.form.CKEDITOR.JS 行 38
‘CKEDITOR.instances[…]’ 为空或不是对象
在代码: setValue : function(value){
Ext.form.TextArea.superclass.setValue.apply(this,[value]);
CKEDITOR.instances[this.id].setData( value );
},
就是这行CKEDITOR.instances[this.id].setData( value );
出现问题
[回复]
晴枫 8月 14th, 2011 下午11:45 回复:
@测试我的, 应该是这时候组件还没有被渲染导致的,建议在load数据前让组件渲染好
[回复]
这个问题解决了,居然ID不能有下划线命名
[回复]
晴枫 8月 14th, 2011 下午11:41 回复:
@测试我的, 还有这样的问题 没碰到过
[回复]
如果加载数据,比如 myform.getForm().load({ ….
会提示错误
ckeditor.js ….. 16行
‘this.$.innerHTML’ 为空或不是对象
[回复]
晴枫 8月 14th, 2011 下午11:41 回复:
@测试我的, 应该是这时候组件还没有被渲染导致的,建议在load数据前让组件渲染好
[回复]
我想问下 ,我用你说的把CKEditor集成到ExtJs中,但是我要给CKeditor添加附加组件的时候,(比如说行距),怎么样都没有反应,自定义插件也不行,是不是什么地方没有配置
[回复]
晴枫 8月 14th, 2011 下午11:40 回复:
@IT信任, 有可能插件引入没有生效,看看插件引入的配置代码是不是被执行到了
[回复]
这个组件有个问题就是如果把这个组件放在window窗体中,第一次打开window窗体可以展示,如果关闭掉window窗口,然后重新打开,此时就无法打开window窗口了
[回复]
晴枫 8月 3rd, 2011 下午7:37 回复:
@rar, 你需要设置window的closeAction为hide,使得窗口关闭后控件不被销毁
[回复]
谢谢大侠的贡献!
问个问题:
在这个ckeditor中编辑文本之后,如何取得其文本呢?
对应的,怎么让它初始时显示特定的文本呢?
期待大侠的解答
[回复]
晴枫 8月 3rd, 2011 下午7:35 回复:
@赵伟雄, 用法和其他的控件一样,比如textfield
[回复]
很奇怪,关闭窗口时会出现container.getChild(…)的错误,
把onDestroy改成
var o = CKEDITOR.instances[this.id];
if (o) o.destroy();
就好了
[回复]
大侠我好,你好大害,我好崇拜你。
另外问下怎么配置工具条。。
我弄了好久没弄明白。可以给点配置工具栏上按钮的代码吗
我的email :23444440@qq.com
谢谢了啊。。
[回复]
晴枫 9月 24th, 2010 下午5:17 回复:
@你好, 末尾增加一段说明,看是否对你有帮助
[回复]
没用过~~一直用notepad++来着
[回复]
晴枫 8月 23rd, 2010 下午8:29 回复:
@老饕, notepad++是桌面应用程序 CKEditor是网页版的的富文本编辑器,不一样
[回复]