所以我试图将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"
答案 0 :(得分:12)
您必须编写自定义绑定处理程序,以便将可观察属性与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?
答案 1 :(得分: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,并且解决方案有效。以下示例使用knockout 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/
答案 2 :(得分: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 );
} );
},
}