我的情况涉及KnockoutJS& CKEditor的。
基本上我们的网站是“单页”应用程序风格的一部分,目前它只涉及2个页面但可能会随着时间的推移而扩展,目前它只是一个“列表”页面和一个“管理”页面列表中的项目。
管理页面本身需要某种富文本编辑器,我们已经将CKEditor用于公司范围的解决方案。
因为这两个页面是“单页”样式,显然CKEditor无法注册管理元素,因为它们在页面加载时不存在 - 足够简单的问题需要修复。因此,作为一个示例,我在一个非常有用的点击事件上附加了CKEditor。接下来的问题是,然后设置的Knockout observable没有得到更新,因为CKEditor实际上并没有修改它附加的textarea,它会创建你实际编辑的所有div / html元素。
经过一段时间的谷歌搜索后,我找到了一个用TinyMCE做这个的例子 - http://jsfiddle.net/rniemeyer/GwkRQ/所以我认为我可以为CKEditor调整类似的东西。
目前我非常接近有一个可行的解决方案,我已经使用这种技术初始化和更新正确的observable(我将在底部发布代码),甚至正确地回发到服务器 - 太棒了。
我目前遇到的问题是“单页”应用部分和CKEditor的重新初始化。
基本上发生了什么,你可以点击列表进行管理然后保存(返回到列表页面)然后当你去另一个'管理'时CKEditor被初始化但它没有任何值,我已经检查了更新代码(下面),'value'肯定有正确的值,但它没有被推送到CKEditor本身。
也许是对CKEditor的流程/初始化过程缺乏了解或者缺乏对挖掘绑定的理解,或者可能是我们单页应用程序设置的框架存在问题 - 我不确定。
以下是代码:
//Test one for ckeditor
ko.bindingHandlers.ckeditor = {
init: function (element, valueAccessor, allBindingsAccessor, context) {
var options = allBindingsAccessor().ckeditorOptions || {};
var modelValue = valueAccessor();
$(element).ckeditor();
var editor = $(element).ckeditorGet();
//handle edits made in the editor
editor.on('blur', function (e) {
var self = this;
if (ko.isWriteableObservable(self)) {
self($(e.listenerData).val());
}
}, modelValue, element);
//handle destroying an editor (based on what jQuery plugin does)
ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
var existingEditor = CKEDITOR.instances[element.name];
existingEditor.destroy(true);
});
},
update: function (element, valueAccessor, allBindingsAccessor, context) {
//handle programmatic updates to the observable
var value = ko.utils.unwrapObservable(valueAccessor());
$(element).html(value);
}
};
所以在HTML中它是一个相当标准的淘汰'data-bind:ckeditor',它在ViewModel初始化时为它应用绑定。
我放了调试器;在代码中查看流程,看起来当我第一次调用init时加载,然后更新,当我第二次进入ko.utils.domNodeDisposal来处理元素时。
我试过不破坏它,然后CKEditor抱怨已存在该名称的东西。我试过不破坏它并检查它是否存在并初始化它是否存在 - 这是第一次有效但第二次我们没有CKEditor。
我认为只有一件我想念的东西会让它发挥作用,但我已经筋疲力尽了。
有没有人知道如何整合这3件可以帮助我的东西?
那里有没有可以帮助我的淘汰专家?
非常感谢任何帮助。
MD
答案 0 :(得分:11)
对于任何有兴趣的人我都对它进行了分类:
所有这一切都是一个基本的执行顺序,我只需要在初始化之前将值设置为textarea html。
注意,这使用jquery适配器扩展来对元素执行.ckeditor()。
可能还有更好的方法来做'模糊'部分。
此扩展目前也不适用于选项,但相比之下应该非常简单。
ko.bindingHandlers.ckeditor = {
init: function (element, valueAccessor, allBindingsAccessor, context) {
var options = allBindingsAccessor().ckeditorOptions || {};
var modelValue = valueAccessor();
var value = ko.utils.unwrapObservable(valueAccessor());
$(element).html(value);
$(element).ckeditor();
var editor = $(element).ckeditorGet();
//handle edits made in the editor
editor.on('blur', function (e) {
var self = this;
if (ko.isWriteableObservable(self)) {
self($(e.listenerData).val());
}
}, modelValue, element);
}
};
答案 1 :(得分:8)
我已经使用了一段时间了,并且再次运行.on(“模糊”)方法的几个问题。也就是说,当人们点击富文本并输入文本然后直接滚动到我的表单上的“保存”按钮时,observable的更新速度不够快。有很多方法可以处理延误,但我想要更正式的东西。我挖到了CKEditor文档,发现了这个gem:focusManager
这是内置功能,可处理焦点和模糊的所有实例,并允许您将真正的模糊事件连接到控件。
这是我的richHandler for rich text then
ko.bindingHandlers.richText = {
init: function (element, valueAccessor, allBindingsAccessor, viewModel) {
var txtBoxID = $(element).attr("id");
var instance = CKEDITOR.instances[txtBoxID];
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);
}
}
答案 2 :(得分:7)
在这里完成其他答案所做的工作是我的解决方案:
change
事件处理更改(按键更新但不仅仅是更新)getData()
,因此您不会像“魔术线”和类似内容那样获得不需要的HTML 代码:
ko.bindingHandlers.ckeditor = {
init: function(element, valueAccessor, allBindingsAccessor, context) {
var options = allBindingsAccessor().ckeditorOptions || {};
var modelValue = valueAccessor();
var value = ko.utils.unwrapObservable(valueAccessor());
$(element).html(value);
$(element).ckeditor();
var editor = $(element).ckeditorGet();
//handle edits made in the editor
editor.on('change', function(e) {
var self = this;
if (ko.isWriteableObservable(self)) {
self($(e.listenerData).val());
}
}, modelValue, element);
//handle disposal (if KO removes by the template binding)
ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
if (editor) {
CKEDITOR.remove(editor);
};
});
},
update: function(element, valueAccessor, allBindingsAccessor, context) {
// handle programmatic updates to the observable
var newValue = ko.utils.unwrapObservable(valueAccessor());
if ($(element).ckeditorGet().getData() != newValue)
$(element).ckeditorGet().setData(newValue)
}
};
我使用的标记(注意afterkeydown
):
<textarea
id="editor1"
data-bind="ckeditor: text, valueUpdate: 'afterkeydown'"
></textarea>
更新:根据评论中的要求,这是一个最小的working Fiddle。
答案 3 :(得分:5)
第一篇帖子让我知道我做错了什么
在我的项目中,我提供了关于是否存在未保存的更改的视觉反馈,因此需要在keyup
上更新可观察的更改。在click
上单击工具栏按钮时。 这也与我在valueUpdate:['afterkeydown','propertychange','input']
属性中使用data-bind
一致。
另外,为了提高性能,我使用了.ckeditor(callback,options)
而不是.on(eventName,handler)
的回调方法参数。
这是我提出的自定义绑定:
ko.bindingHandlers.ckeditor = {
init: function (element, valueAccessor, allBindingsAccessor, context) {
// get observable
var modelValue = valueAccessor();;
$(element).ckeditor(function(textarea) {
// <span> element that contains the CKEditor markup
var $ckeContainer = $(this.container.$);
// <body> element within the iframe (<html> is contentEditable)
var $editorBody =
$ckeContainer.find('iframe').contents().find('body');
// sets the initial value
$editorBody.html( modelValue() );
// handle edits made in the editor - by typing
$editorBody.keyup(function() {
modelValue( $(this).html() );
});
// handle edits made in the editor - by clicking in the toolbar
$ckeContainer.find('table.cke_editor').click(function() {
modelValue( $editorBody.html() );
});
});
// when ko disposes of <textarea>, destory the ckeditor instance
ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
$(element).ckeditorGet().destroy(true);
});
},
update: function (element, valueAccessor, allBindingsAccessor, context) {
// handle programmatic updates to the observable
var newValue = ko.utils.unwrapObservable(valueAccessor());
var $ckeContainer = $(element).ckeditorGet().container;
if( $ckeContainer ) {
// <span> element that contains the CKEditor markup
$ckeContainer = $($ckeContainer.$);
// <body> element within the iframe (<html> is contentEditable)
var $editorBody =
$ckeContainer.find('iframe').contents().find('body');
// if new value != existing value, replace it in the editor
if( $editorBody.html() != newValue )
$editorBody.html( newValue );
}
}
};
<强>理由:强>
我知道我应该使用.getData()
和.setData(html)
,而不是在<body>
内容中找到<table class="cke_editor">
和iframe
这种相当粗俗的方式。
原因是update:
条件:
if( $(element).ckeditorGet().getData() != newValue )
$(element).ckeditorGet().setData( newValue )
由于CKEditor所做的HTML格式化,最初是正确的。因此,即使不是,也会通知用户有关脏记录的信息。 非常具体我所以我认为你应该知道,万一你想知道为什么。
答案 4 :(得分:0)
我刚用CKEditor 4使用这种技术用双向绑定覆盖现有的(单向)“html”绑定。我正在使用内联CKEditor,它可能与完整/静态编辑器的行为不同(不确定)。我从“值”绑定开始,并将其调整为使用innerHTML
代替:
ko.bindingHandlers.html = {
'init': function (element, valueAccessor, allBindingsAccessor) {
var eventsToCatch = ["blur"];
var requestedEventsToCatch = allBindingsAccessor()["valueUpdate"];
var valueUpdateHandler = null;
if (requestedEventsToCatch) {
if (typeof requestedEventsToCatch == "string")
requestedEventsToCatch = [requestedEventsToCatch];
ko.utils.arrayPushAll(eventsToCatch, requestedEventsToCatch);
eventsToCatch = ko.utils.arrayGetDistinctValues(eventsToCatch);
}
valueUpdateHandler = function () {
var modelValue = valueAccessor();
var oldValue = ko.utils.unwrapObservable(modelValue);
var elementValue = element.innerHTML;
var valueHasChanged = (oldValue !== elementValue);
if (valueHasChanged)
modelValue(elementValue);
}
ko.utils.arrayForEach(eventsToCatch, function (eventName) {
var handler = valueUpdateHandler;
if (eventName.indexOf("after") == 0) {
handler = function () {
setTimeout(valueUpdateHandler, 0)
};
eventName = eventName.substring("after".length);
}
ko.utils.registerEventHandler(element, eventName, handler);
});
},
'update': function (element, valueAccessor) {
var newValue = ko.utils.unwrapObservable(valueAccessor());
var elementValue = element.innerHTML;
var valueHasChanged = (newValue !== elementValue);
if (valueHasChanged)
element.innerHTML = newValue;
}
};
警告:这应该更新为使用CKEditor自己的change
事件。
答案 5 :(得分:0)
我重写了这个以更新每个keyup上的observable,而不是模糊。这是对observable的更多更新,但只要您使用按钮保存,这似乎到目前为止工作得很好!
//handle edits made in the editor
CKEDITOR.instances.thread_message.on('contentDom', function() {
CKEDITOR.instances.thread_message.document.on('keyup', function(e) {
var self = this;
if (ko.isWriteableObservable(self)) {
var ckValue = CKEDITOR.instances.element_id.getData();
self(ckValue);
//console.log("value: " + ckValue);
}
}, modelValue, element);
});
答案 6 :(得分:-1)
对于'模糊'部分,我尝试了下面的代码,似乎可以正常工作
editor.on('blur', function (e) {
var self = this;
if (ko.isWriteableObservable(self)) {
var ckValue = e.editor.getData();
self(ckValue);
}
}, modelValue, element);
我认为如果你从其他地方“更新”observable,那么仍然需要'更新'部分(不是通过编辑,因为这是由'模糊'处理的)