6

所以我正在尝试将 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"

4

3 回答 3

12

You have to write your custom binding handler in order for your observable property to be linked with an instance of CKEditor.

First, you could start from the custom binding found here. One of the posts contains a custom binding, though I'm not sure it works. You have to check. I copied it down here, credits do not go to me of course:

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);
    }
} 

A typical use then would be in the HTML:

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

Secondly, you could check out the custom binding handler for TinyMCE initially written by Ryan Niemeyer and updated by other talented people. Maybe TinyMCE could work out for you instead of CKEditor ?

于 2013-04-29T17:35:14.863 回答
3

要回答您的具体问题,您需要跟踪编辑的来源,以免两次触发更新。当 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/

于 2013-10-18T03:36:07.250 回答
0

任何尝试使用 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 );
        } );
    },
}
于 2019-02-09T22:57:53.273 回答