如何在将 ckeditor 数据绑定为 knockout observable 时更新它?

How to update ckeditor data when binding it as knockout observable?

我正在尝试将 ckeditor 作为 knockout.js 可观察对象使用,但我 运行 遇到了一些麻烦。 首先,这是我的代码:

ko.bindingHandlers.CKEDITOR = {
init: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
         var ckEditorValue = valueAccessor();
         var id = $(element).attr('id');
         var options = allBindings().EditorOptions;
         var instance = CKEDITOR.replace(id, {
             on: {
                 change: function () {
                     // This moves the caret to the start of the editor on each key pressed
                     ckEditorValue(instance.getData());
                 }
             }
         });
         // instance.setData(ckEditorValue());
     },
update: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
         var id = $(element).attr('id');
         var ckEditorValue = valueAccessor();
         CKEDITOR.instances[id].setData(ckEditorValue());
     }
};

我在使用这段代码时遇到的问题是,在我按下的每个键上,change 事件正在触发,插入符号移动到编辑器的顶部开始位置。 我尝试调用 blur 事件而不是 change 事件,但是当我单击保存按钮时它不会触发,它仅在我单击空白位置或移动到另一个控件时触发。

我怎样才能使我的可观察对象保持更新?

我有 a similar issue with a different editor,并通过删除 update 处理程序并将其替换为手动订阅来解决它。这样,您可以引入一些共享状态 'skip this update, that was me':

ko.bindingHandlers.CKEDITOR = {
  init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
    var ckEditorValue = valueAccessor();
    var id = $(element).attr('id');
    var options = allBindings().EditorOptions;
    var ignoreChanges = false;

    var instance = CKEDITOR.replace(id, {
      on: {
        change: function() {
          ignoreChanges = true;
          ckEditorValue(instance.getData());
          ignoreChanges = false;
        }
      }
    });

    ckEditorValue.subscribe(function(newValue) {
      if (!ignoreChanges) {
        instance.setData(newValue);
      }
    });

  }
};

这是 CKEditor v4.6.2 (12 Jan 2017) 的扩展 KO 绑定。 我想使用 'update' 的预期行为 这将确保可观察值在 'blur' 事件上更新。

这花了一些时间来微调,所以我希望这对某人有用。

/**
*   Custom KO binding for jQuery CKEditor plugin v4.2.6
*   {@link http://docs.ckeditor.com/#!/guide/dev_jquery Original Reference}
*   Usage: <div data-bind="ckeditor: { [ CKEditor init options should be placed here.] }, value: $data.value"></div>
*/
ko.bindingHandlers.ckeditor = {
    init: function (element, valueAccessor, allBindingsAccessor) {
        var options = valueAccessor();
        var binding = ko.utils.unwrapObservable(allBindingsAccessor()).value;

        options = ko.utils.extend(ko.bindingHandlers.ckeditor.options, options);

        // Set an ID if the element doesn't already have one assigned
        var elemId = $(element).attr('id');
        if(typeof elemId === "undefined") {
            elemId = 'ck_' + Math.random().toString(36).substring(7);
            $(element).attr('id', elemId);
        }

        var ckeditorId = $(element).attr("id");

        ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
            var ckInst = eval( "CKEDITOR.instances." + ckeditorId );
            if ( ckInst ) ckInst.destroy();
        });

        $(element).ckeditor( options );

        // wire up the source button click event to copy the value
        CKEDITOR.instances[ckeditorId].on('afterCommandExec', handleAfterCommandExec);
        function handleAfterCommandExec(event) {
            var commandName = event.data.name;
            // For 'source' commmand
            if (commandName == 'source')
                //console.log("source button pressed!");
                binding( $(element).val() );
        }

        // wire up the blur event to ensure our observable is properly updated
        CKEDITOR.instances[ckeditorId].focusManager.blur = function () {
            var ckInst = eval( "CKEDITOR.instances." + ckeditorId );
            //console.log('blur!');
            //console.log( $(element).val() );
            binding( $(element).val() );
        };

    },
    update:  function (element, valueAccessor, allBindingsAccessor) {
        var value = ko.unwrap(allBindingsAccessor.get('value'));

        // only update the code when there are differences
        if ( value !== $(element).val() ) {
            $(element).val(value);
        }
    },
    options: {
        toolbar: [
            { name: 'styles', items: [ 'Styles', 'Format' ] },
            { name: 'document', groups: [ 'mode', 'document', 'doctools' ], items: [ 'Source' ] },
            { name: 'clipboard', groups: [ 'undo' ], items: [ 'Undo', 'Redo' ] },
            { name: 'editing', groups: [ 'find', 'selection', 'spellchecker' ], items: [ 'Scayt' ] },
            { name: 'tools', items: [ 'Maximize' ] },
            { name: 'others', items: [ '-' ] },
            { name: 'about', items: [ 'About' ] },
            '/',
            { name: 'basicstyles', groups: [ 'basicstyles', 'cleanup' ], items: [ 'Bold', 'Italic', 'Underline', 'Strike', 'Subscript', 'Superscript', '-', 'RemoveFormat' ] },
            { name: 'paragraph', groups: [ 'list', 'indent', 'blocks', 'align', 'bidi' ], items: [ 'NumberedList', 'BulletedList', '-', 'Outdent', 'Indent', '-', 'Blockquote' ] },
            { name: 'links', items: [ 'Link', 'Unlink', 'Anchor' ] },
            { name: 'insert', items: [ 'Image', 'Table', 'HorizontalRule', 'SpecialChar' ] }
        ]
    }
};

我知道我回答这个问题有点晚了,但我已经为此苦苦挣扎了一段时间,我的问题是我需要用户在框中输入或通过按钮添加模板,所有我找到的解决方案解决了其中一个问题,但没有同时解决这两个问题。

下面的 fiddle 解决了在框中键入(将光标保持在正确的位置并且不重置到位置 0)以及单击按钮以触发 KO 函数以更新编辑器中的数据.

这是实现此目的的自定义绑定

    var TOOLBAR_CONFIG = [{
    name: 'document',
    items: ['Source', '-', 'Save', 'NewPage', 'DocProps', 'Preview', 'Print', '-', 'Templates']
  }, {
    name: 'clipboard',
    items: ['Cut', 'Copy', 'Paste', 'PasteText', 'PasteFromWord', '-', 'Undo', 'Redo']
  }, {
    name: 'editing',
    items: ['Find', 'Replace', '-', 'SelectAll', '-', 'SpellChecker', 'Scayt']
  }, {
    name: 'forms',
    items: ['Form', 'Checkbox', 'Radio', 'TextField', 'Textarea', 'Select', 'Button', 'ImageButton',
      'HiddenField'
    ]
  },
  '/', {
    name: 'basicstyles',
    items: ['Bold', 'Italic', 'Underline', 'Strike', 'Subscript', 'Superscript', '-', 'RemoveFormat']
  }, {
    name: 'paragraph',
    items: ['NumberedList', 'BulletedList', '-', 'Outdent', 'Indent', '-', 'Blockquote', 'CreateDiv',
      '-', 'JustifyLeft', 'JustifyCenter', 'JustifyRight', 'JustifyBlock', '-', 'BidiLtr', 'BidiRtl'
    ]
  }, {
    name: 'links',
    items: ['Link', 'Unlink', 'Anchor']
  }, {
    name: 'insert',
    items: ['Image', 'Flash', 'Table', 'HorizontalRule', 'Smiley', 'SpecialChar', 'PageBreak', 'Iframe']
  },
  '/', {
    name: 'styles',
    items: ['Styles', 'Format', 'Font', 'FontSize']
  }, {
    name: 'colors',
    items: ['TextColor', 'BGColor']
  }, {
    name: 'tools',
    items: ['Maximize', 'ShowBlocks', '-', 'About']
  }
];

ko.bindingHandlers.ckeditor = {
  init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
  var ignoreChanges = false;
    var options = ko.utils.extend({
      toolbar: TOOLBAR_CONFIG,
      removePlugins: 'elementspath'
    }, allBindings.get('ckeditorOptions') || {});
    var modelValue = valueAccessor();

    var editor = CKEDITOR.replace(element, options);

    editor.on('change', function() {
    ignoreChanges = true;
      modelValue(editor.getData());
      ignoreChanges = false;
    });

modelValue.subscribe(function(newValue) {
var editor = new CKEDITOR.dom.element(element).getEditor();
if (!(ignoreChanges))
       editor.setData(newValue);
    });

    //handle disposal (if KO removes by the template binding)
    ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
      if (editor) {
        CKEDITOR.remove(editor);
      };
    });
  },
    update: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
        var editor = new CKEDITOR.dom.element(element).getEditor();
        console.log(ko.unwrap(valueAccessor()));
        editor.setData(ko.unwrap(valueAccessor()), null, true);
    }
};

工作示例

http://jsfiddle.net/gr71bzwx/10/