在CKEditor的instanceReady上调用时,setReadOnly会导致错误

时间:2013-08-22 22:01:53

标签: javascript jquery gwt backbone.js ckeditor

我正在尝试在实例完全加载后将我的CKEditor实例设置为“readOnly”,但我收到一个Javascript错误:Cannot call method 'setReadOnly' of null。当我深入研究时,错误来自ckeditor.js中的这一行,在editor.setReadOnly方法中:this.editable().setReadOnly(a);这意味着编辑器存在,但editable方法/属性(在CKEditor实例上)没有。

下面是我的代码,我会稍微解释一下。我的应用程序是GWT和Backbone的组合。 CKEditor本身是由Backbone代码创建的,但父元素在GWT中,所以我在那里发起setEnabled动作。

private native void setEnabledOnLoad(boolean enabled, String id) /*-{
  CKEDITOR.on("instanceReady", function(evt) {
    if(evt.editor.name === id) {
      Namespace.trigger(Namespace.Events.SET_ENABLED, enabled);
    }
  });
}-*/;

setEnabled: function(enabled) {
  this.editor.setReadOnly(!enabled);
  if(enabled){
    this.editor.focusManager.focus();
  } else {
    this.editor.focusManager.blur();
  }
}

Backbone类有Namespace.Events.SET_ENABLED的监听器,可以触发setEnabled

我应该收听另一个CKEditor事件吗? instanceReady上似乎没有editable个活动。我错过了什么?

修改
this.editor在Backbone类render函数中创建,如下所示:

this.editor = CKEDITOR.replace(this.$(this.id)[0], config);

我在创建之后不立即添加instanceReady侦听器的原因是因为在实例完全初始化之前在GWT中调用了函数setEnabledOnLoad。这是将代码放在两个地方的结果。 GWT已经说“好了,创建了实例”,但是当GWT进入下一行代码并希望将其设置为启用/禁用时,Backbone还没有完成。

3 个答案:

答案 0 :(得分:8)

两年后,但这是我的解决方案。也许别人会觉得它很有用。 如上所述,事件在完全设置editable()函数之前被触发,因此一种解决方案是在将其设置为只读之前等待它完成。这可能是一种丑陋的方式,但它确实有效。

        //Delayed execution - ckeditor must be properly initialized before setting readonly
        var retryCount = 0;
        var delayedSetReadOnly = function () {
            if (CKEDITOR.instances['bodyEditor'].editable() == undefined && retryCount++ < 10) {
                setTimeout(delayedSetReadOnly, retryCount * 100); //Wait a while longer each iteration
            } else {
                CKEDITOR.instances['bodyEditor'].setReadOnly();
            }
        };
        setTimeout(delayedSetReadOnly, 50);

答案 1 :(得分:5)

您可以尝试以这种方式订阅instanceReady事件:

CKEDITOR.instances.editor.on("instanceReady", onInstanceReadyHandler) 

但是,当时必须已创建editor实例(在调试器中检查CKEDITOR.instances)。

我对editableeditor之间的区别感到有点困惑。您能否在this.editorthis.editable被分配的位置显示代码片段?

[编辑] 我想我知道发生了什么。 CKEDITOR是一个全局对象,您可以将其视为包含所有CKEDITOR实例的类。尝试使用CKEDITOR.on处理事件是不对的,您需要在特定实例上执行此操作(如上所示)。我假设,“editor”是您想要附加CKEDITOR实例的父元素的ID(如果我错了,请纠正我)。我不熟悉Backbone,但通常用replace完成:

var editorInstance = CKEDITOR.replace("editor", { on: { 
    instanceReady: function(ev) { alert("editor is ready!"); }}});

这里我们将一个新的CKEDITOR实例附加到editor父元素,并同时订阅instanceReady事件。返回的对象editorInstance应该提供您可能需要的所有APIs,包括setReadOnly。您还可以使用父元素ID(即CKEDITOR)通过全局CKEDITOR.instances.editor对象访问它。另一方面,editableeditor上可用的服务对象。我想不出你可能需要直接使用它的任何具体情况。

答案 2 :(得分:1)

我为从未使用我的解决方案更新此内容而道歉。我需要将GWT函数与CKEditor行为进一步解耦。所以,我在GWT'setEnabled'中添加了一个函数,该函数在想要更新CKEditor对象的启用状态时从父对象调用。

public void setEnabled(boolean enabled) {
    this.enabled = enabled;
    toggleCKEditorEnabled(enabled);     
}       

然后将'setEnabledOnLoad'上面引用的函数更改为'toggleCKEditorEnabled',它触发具有enabled值的SET_ENABLED事件。

我没有将侦听器附加到CKEditor的特定实例,而是添加到Backbone MessageEntryView类中,该类是CKEditor实例的容器。在MessageEntryView的initialize函数中,我添加了这一行

Namespace.on(Namespace.Events.SET_ENABLED, this.setEnabled);

这只能起作用,因为我在任何给定时间都在屏幕上加载了一个CKEditor实例。这个问题及其解决方案使我们无法一次向页面添加更多CKEditor实例,这是我们在继续使用Backbone替换整个客户端之前所讨论的内容。