【问题标题】:Inline CKEditor integration with knockoutjs内联 CKEditor 与 knockoutjs 的集成
【发布时间】:2013-04-29 17:13:43
【问题描述】:

所以我正在尝试将 CKEditor 的内联编辑与 Knockout.js 集成。我能够成功加载 CKEditor 和 knockout.js。

我似乎无法让 ko.observable 更新属性:

<script type="text/javascript">

    var viewModel = function () {
        var self = this;
        self.editorText = ko.observable('ABC');
        self.testNewValue = function () {
            console.log(this.editorText());
        };
    }

    ko.applyBindings(new viewModel());
</script>

这里是html:

<div id="editable" contenteditable="true" data-bind="html: editorText">
</div>
<div>
    <input type="button" data-bind="click: testNewValue" value="test" />
</div>

console.log 结果始终显示“ABC”,无论您是否更新它。注意:我也试过data-bind="text: editorText"

【问题讨论】:

标签: jquery knockout.js ckeditor


【解决方案1】:

您必须编写自定义绑定处理程序才能将您的可观察属性与 CKEditor 的实例链接。

首先,您可以从找到here 的自定义绑定开始。其中一篇文章包含自定义绑定,但我不确定它是否有效。你必须检查。我把它复制到这里,学分当然不属于我:

ko.bindingHandlers.ckEditor = {

    init: function (element, valueAccessor, allBindingsAccessor, viewModel) {
        var txtBoxID = $(element).attr("id");
        var options = allBindingsAccessor().richTextOptions || {};
        options.toolbar_Full = [
            ['Source', '-', 'Format', 'Font', 'FontSize', 'TextColor', 'BGColor', '-', 'Bold', 'Italic', 'Underline', 'SpellChecker'],
            ['NumberedList', 'BulletedList', '-', 'Outdent', 'Indent', '-', 'Blockquote', 'CreateDiv', '-', 'JustifyLeft', 'JustifyCenter', 'JustifyRight', 'JustifyBlock', '-', 'BidiLtr', 'BidiRtl'],
            ['Link', 'Unlink', 'Image', 'Table']
        ];

        // handle disposal (if KO removes by the template binding)
        ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
            if (CKEDITOR.instances[txtBoxID]){ 
                CKEDITOR.remove(CKEDITOR.instances[txtBoxID]); 
            }
        });

        $(element).ckeditor(options);

        // wire up the blur event to ensure our observable is properly updated
        CKEDITOR.instances[txtBoxID].focusManager.blur = function () {
            var observable = valueAccessor();
            observable($(element).val());
        };
    },
    update: function (element, valueAccessor, allBindingsAccessor, viewModel) {
        var val = ko.utils.unwrapObservable(valueAccessor());
        $(element).val(val);
    }
} 

一个典型的用途是在 HTML 中:

<textarea id="txt_viewModelVariableName" 
          data-bind="ckEditor: viewModelVariableName"></textarea>

其次,您可以查看最初由 Ryan Niemeyer 编写并由其他有才华的人更新的 custom binding handler for TinyMCE。也许 TinyMCE 可以代替 CKEditor 为您工作?

【讨论】:

  • 错了,我真的不知道。我首先需要去 CKEditor4 的网站了解“内联编辑”是什么意思 :-) 但最好的方法是编写代码 :-)
  • Steve Sanderson 是 knockoutJs 的创造者,Ryan Niemeyer 是一个了不起的人。
  • 这行得通,但不要忘记包含最新 ckeditor 的 jQuery 适配器,正如它在 nightly.ckeditor.com/14-09-02-06-07/standard/samples/… 中提到的那样。否则 $(...).ckeditor 调用将失败。
  • 至少在 CKEditor 4.4.7 上,我发现最好使用“更改”事件,以确保在每种情况下都更新淘汰赛绑定,而不是“模糊”。例如,如果您将文本复制/粘贴到 CKEditor 窗口中,然后立即按下按钮来保存底层的 observable。刚刚遇到这个问题,'change' 事件在每种情况下都捕获了适当的更新。
  • 这很好用,只要记住以正确的顺序包含 jquery 适配器即可。首先是 jquery,然后是 ckeditor,然后是 jquery 适配器,最后是绑定处理程序。适配器位于 ckeditor 下载的“适配器”文件夹中。
【解决方案2】:

要回答您的具体问题,您需要跟踪编辑的来源,以免两次触发更新。当 observable 不是从编辑器更新时,您不希望编辑器中的突然变化重新更新 observable。当编辑器更新 observable 时,同样的想法,你不希望 observable 再次通知编辑器。我曾经使用布尔值来跟踪它们。与编辑器无关的代码如下:

var isObservableChange = false;
var isEditorChange = false;

editor.change = function () {
    if(!isObservableChange){
        isEditorChange = true;
        observable(editor.data);
        isEditorChange = false;
    }
};

observable.subscribe(function (newValue) {
    if(!isEditorChange){
        isObservableChange = true;
        editor.data = observable();
        isObservableChange = false;
    }
});

我有一个项目,我正在尽最大努力使用 CKEditor 进行内联编辑。我最终放弃并尝试了使用相同类型的代码的 TinyMCE,并且解决方案有效。以下示例使用淘汰赛 2.3.0、tinymce 4.0.8 和 jquery 1.10.2。 jquery 可以替换为常规文档 id 访问,但我使用 jquery 作为快速代码的拐杖。绑定代码如下:

ko.bindingHandlers.wysiwyg = {
    init: function (element, valueAccessor, allBindingsAccessor, viewModel) {
        var value = valueAccessor();
        var valueUnwrapped = ko.unwrap(value);
        var allBindings = allBindingsAccessor();
        var $element = $(element);
        $element.attr('id', 'wysiwyg_' + Date.now());
        if (ko.isObservable(value)) {
            var isSubscriberChange = false;
            var isEditorChange = true;
            $element.html(value());
            var isEditorChange = false;

            tinymce.init({
                selector: '#' + $element.attr('id'),
                inline: true,
                plugins: [
                    "advlist autolink lists link image charmap print preview anchor",
                    "searchreplace visualblocks code fullscreen",
                    "insertdatetime media table contextmenu paste"
                ],
                toolbar: "insertfile undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image",
                setup: function (editor) {
                    editor.on('change', function () {
                        if (!isSubscriberChange) {
                            isEditorChange = true;
                            value($element.html());
                            isEditorChange = false;
                        }
                    });
                }
            });
            value.subscribe(function (newValue) {
                if (!isEditorChange) {
                    isSubscriberChange = true;
                    $element.html(newValue);
                    isSubscriberChange = false;
                }
            });
        }
    }
}

要使用它,只需将它绑定到一个 div。像这样

<div data-bind="wysiwyg: test"></div>

可以在这里找到一个工作示例http://jsfiddle.net/dhQk/2xjKc/ 我希望这会有所帮助。

编辑:

看起来 CKEditor 版本毕竟可以工作。我只需要使用不同的cdn。链接是http://jsfiddle.net/dhQk/CSwr6/

【讨论】:

    【解决方案3】:

    任何尝试使用 ckeditor 5 执行此操作的人,以下工作(旧版应用程序)

    ko.bindingHandlers.wysivalue = {
        init: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
            let showToolbarBinding = allBindings.get('toolbar');
            let showToolbar = ko.unwrap(showToolbarBinding);
            let ckConfig=showToolbar?{toolbar: ["undo", "redo", "bold", "italic", "blockQuote", "ckfinder",  "heading", "link", "numberedList", "bulletedList", "insertTable", "tableColumn", "tableRow", "mergeTableCells"]}
            :{toolbar: []};
    
            let heightBinding = allBindings.get('height');
            let height = ko.unwrap(heightBinding);
            if (!height) height='250px'
    
            let isReadOnlyBinding = allBindings.get('readOnly');
            var isReadOnly = ko.unwrap(isReadOnlyBinding);
    
            let onFieldDataChanged = allBindings.get('onFieldDataChanged');
    
            var value = valueAccessor();
            var valueUnwrapped = ko.unwrap(value);
    
            ClassicEditor
            .create(element, ckConfig )
            .then( editor => {
                // var TableList=Array.from( editor.ui.componentFactory.names() );
                editors.set( element, editor );
                editor.isReadOnly = isReadOnly;
                editor.ui.view.editable.editableElement.style.height = height;
                if (ko.isObservable(value)) {
                    var isSubscriberChange = false;
                    var isEditorChange = true;
                    editor.setData(valueUnwrapped);
                    var isEditorChange = false;
    
                    editor.model.document.on( 'change:data', (eventInfo, eventName, newValue, oldValue) => {
                        if (!isSubscriberChange) {
                            isEditorChange = true;
                            value(editor.getData());
                            if (onFieldDataChanged) onFieldDataChanged();
                            isEditorChange = false;
                        }
                    } );
    
                    value.subscribe(function (newValue) {
                        if (!isEditorChange) {
                            isSubscriberChange = true;
                            editor.setData(newValue);
                            isSubscriberChange = false;
                        }
                    });
                }
            } )
            .catch( error => {
                console.error( error );
            } );
        },
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-12-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-12-22
      • 2020-02-17
      • 1970-01-01
      相关资源
      最近更新 更多