I am having a select2 box. On selecting a value, is it possible to include the optgroup name to the selected value like below?
Expected Result
I was able to figure it out. Not sure if it will work on all cases, but it works for multi select. Posting the code as it may be useful for someone.
$(document).ready(function () {
function formatState(state) {
console.log(state);
if (!state.id) {
return state.text;
}
var parent = state.element.parentElement;
var $state = parent.label + "->" + state.text;
return $state;
}
$('.js-example-basic-single').select2({
data: data,
maximumSelectionLength: 1,
templateSelection: formatState
});
});
First up: check this fiddle.
I have sortable array of elements created with the Knockout sortable library. When I initially apply the binding the cleditor initializes fine.
However, when sortable elements are sorted, the cleditor fails to re-initialize (I'm not sure what happens but cleditor fails). The cleditor just displays "true" instead of actual value in Firefox, and nothing in all other browsers.
I'm trying to figure out where the problem is, whether it is on the custom binding, or jQuery-UI, or the Knockout sortable library?
I am not recieving any errors in my console.
ko.bindingHandlers.cleditor = {
init: function(element, valueAccessor, allBindingsAccessor) {
var modelValue = valueAccessor(),
allBindings = allBindingsAccessor();
var $editor = jQuery(element).cleditor({
height: 50,
controls: "bold italic underline | bullets numbering | undo redo"
});
$editor[0].change(function() {
var elementValue = $editor[0].doc.body.innerHTML;
if (ko.isWriteableObservable(modelValue)) {
modelValue(elementValue);
} else {
if (allBindings['_ko_property_writers'] && allBindings['_ko_property_writers'].cleditor) {
allBindings['_ko_property_writers'].cleditor(elementValue);
}
}
});
},
update: function(element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor()) || '',
$editor = jQuery(element).cleditor();
if ($editor[0].doc.body.innerHTML !== value) {
//$editor[0].doc.body.innerHTML = value;
$editor[0].doc.body.innerHTML = value;
$editor[0].focus();
}
}
};
How can I make the cleditor to work, even after the elements are sorted?
I found this resource, but I couldn't find anything wrong in code as said in that topic.
The link you provided was helpful. The CLEditor refresh method is the right way to update it after it's dragged. It just needs to be done at the correct time, using the sortable stop event.
stop: function(event, ui) {
$(ui.item).find("textarea").cleditor()[0].refresh();
}
http://jsfiddle.net/mbest/rh8c2/1/
I also worked to integrate this into your cleditor binding. In the init function:
jQuery(document).on('sortstop', function(event, ui) {
if (jQuery.contains(ui.item[0], element)) {
jQuery(element).cleditor()[0].refresh();
}
});
I also made a change in the update function to keep the <textarea> value in sync, because refresh updates the editor's value from the <textarea>:
$editor[0].updateTextArea();
http://jsfiddle.net/mbest/jw7Je/7/
Why doesn't the list auto update as I type? No matter what I type the list is the same.
I would like to be able to type "wj" and it will only list items starting with "wj".
JS
$(function()
{
$("#part_num_id").autocomplete({
source: "partnum/get"
});
});
Controller
public function get()
{
$q = $this->input->post('term', TRUE);
$this->load->model('partnum_model');
$this->partnum_model->get_partnum2($q);
}
Model
function get_partnum2($q)
{
$this->db->select('*');
$this->db->like('part_num', $q);
$query = $this->db->get('item_history');
if($query->num_rows > 0)
{
foreach ($query->result_array() as $row)
{
$new_row['label']=htmlentities(stripslashes($row['part_num']));
$new_row['value']=htmlentities(stripslashes($row['id']));
$row_set[] = $new_row; //build an array
}
echo json_encode($row_set);
}
}
I may be wrong since I haven't thoroughly tested this,
but I believe the autocomplete sends a GET request, not a POST request.
Therefore
$q = $this->input->post('term', TRUE);
won't bring you anything; Instead you should write
$q = $this->input->get('term', TRUE);
I have a situation involving KnockoutJS & CKEditor.
Basically we've got part of our site that is 'single page' app style, currently it just involves 2 pages but will likely expand over time, currently it's just a 'listings' page and a 'manage' page for the items in the list.
The manage page itself requires some sort of rich text editor, we've gone with CKEditor for a company wide solution.
Because these 2 pages are 'single page' style obviously CKEditor can't register against the manage elements because they aren't there on page load - simple enough problem to fix. So as a sample I attached CKEditor on a click event which worked great. The next problem was that then the Knockout observables that had been setup weren't getting updated because CKEditor doesn't actually modify the textarea it's attached too it creates all these div's/html elements that you actually edit.
After a bit of googleing I found an example of someone doing this with TinyMCE - http://jsfiddle.net/rniemeyer/GwkRQ/ so I thought I could adapt something similar to this for CKEditor.
Currently I'm quite close to having a working solution, I've got it initialising and updating the correct observables using this technique (I'll post code at the bottom) and even posting back to the server correctly - fantastic.
The problem I'm currently experiencing is with the 'Single Page' app part and the reinitialisation of CKEditor.
Basically what happens is you can click from list to manage then save (which goes back to the list page) then when you go to another 'manage' the CKEditor is initialised but it doesn't have any values in it, I've checked the update code (below) and 'value' definitely has the correct value but it's not getting pushed through to the CKEditor itself.
Perhaps it's a lack of understanding about the flow/initialisation process for CKEditor or a lack of understanding about knockout bindings or perhaps it's a problem with the framework that's been setup for our single page app - I'm not sure.
Here is the code:
//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);
}
};
So in the HTML it's a fairly standard knockout 'data-bind: ckeditor' that applyies the bindings for it when the ViewModel is initialised.
I've put debugger; in the code to see the flow, it looks like when I load the first time it calls init, then update, when I go in the second time it hits the ko.utils.domNodeDisposal to dispose of the elements.
I've tried not destroying it which CKEditor then complains that something already exists with that name. I've tried not destroying it and checking for if it exists and initialising if it doesn't - that works the first time but the second time we have no CKEditor.
I figure there's just one thing I'm missing that will make it work but I've exhausted all options.
Does anyone have any knowledge on integrating these 3 things that can help me out?
Are there any knockout experts out there that might be able to help me out?
Any help would be much appreciated.
MD
For anyone interested I sorted it:
All it was was a basic order of execution, I just needed to set the value to the textarea html before it got initialised.
Note this uses a jquery adaptor extension to do the .ckeditor() on the element.
There is probably also a better way to do the 'blur' part.
This extension also doesn't work with options at the moment but that should be quite simple in comparison.
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);
}
};
I've been working with this for a while now and ran again several problems with the .on("blur") approach. Namely, when people clicked in to the rich text and entered text then scrolled directly to the Save button on my form, the observable didn't get updated fast enough. There are a ton of ways to handle delays, but I wanted something more official. I dug in to the CKEditor documentation and found this gem: focusManager
This is built-in functionality that handles all the instances of focus and blur and allows you to hook up a true blur event to the control.
Here is my bindingHandler 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);
}
}
Building up on the work done in the other answers here's my solution:
handles changes using ckeditor's own change event (updates on keypress but not just that)
uses ckeditor's getData() so you don't get unwanted HTML like "magic line" and similar stuff
handles memory management (untested)
Code:
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)
}
};
The markup I use (note the afterkeydown):
<textarea
id="editor1"
data-bind="ckeditor: text, valueUpdate: 'afterkeydown'"
></textarea>
Update: as requested in the comments, here is a minimal working Fiddle.
First post so let me know if I've done anything wrong
In my project, I gave visual feedback as to whether there were unsaved changes and so needed the observable updated on keyup. And on click for when a toolbar button was clicked. This also was consistent with me using valueUpdate:['afterkeydown','propertychange','input'] in my data-bind attributes.
Also, for performance, I used the callback method parameter of .ckeditor(callback,options) rather than .on(eventName,handler).
This is the custom binding I came up with:
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 );
}
}
};
Justification:
I know I should probably use .getData() and .setData(html) instead of this rather hacky way of finding <body> and <table class="cke_editor"> within the iframe contents.
Reason being, for update: the condition within:
if( $(element).ckeditorGet().getData() != newValue )
$(element).ckeditorGet().setData( newValue )
was initially true due to HTML formatting that CKEditor does. And so, notified the user about a dirty record even though it wasn't. Very specific to me so I thought you should know, in case you were wondering why.
I just used this technique with CKEditor 4 to overwrite the existing (1-way) "html" binding with a 2-way binding. I'm using the inline CKEditor which may behave differently (not sure) than the full/static editor. I started with the "value" binding and tweaked it to work with the innerHTML instead:
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;
}
};
Caveat: This should probably be updated to use CKEditor's own change event.
I rewrote this to update the observable on each keyup, instead of on blur. This is a lot more updates to the observable, but as long as you are saving with a button, this seems to work great so far!
//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);
});
For the 'blur' part, I tried the code below and it seems to work
editor.on('blur', function (e) {
var self = this;
if (ko.isWriteableObservable(self)) {
var ckValue = e.editor.getData();
self(ckValue);
}
}, modelValue, element);
I think the 'Update' part is still needed if you "update" the observable from somewhere else (not through editing as this is taken care of by the 'blur')
I have based my code on this example
http://jsfiddle.net/rniemeyer/WpnTU/
When you select an item I want the title of the dialog to have a observable's value
I managed to to it by creating another custom binding
ko.bindingHandlers.dialogOptions = {
update: function(element, valueAccessor) {
var options = ko.utils.unwrapObservable(valueAccessor());
if (options ) {
$(element).dialog(options);
}
}
}
Added a new observable to viewmodel and set it when the item is selected
this.selectProduct = function(product) {
self.dialogOptions({ title: product.name() });
self.selectedProduct(product);
}
Working example: http://jsfiddle.net/WpnTU/76/
It works but I do not like it, it adds a new observable which is very coupled with the GUI, it would be much nicer if I could use the already exiting selectProduct observable and point out the name property in the GUI something like { title: selectProduct.name }
Here is a sample that moves the .dialog calls into the update function and unwraps the options, so that it will be triggered any time that something changes.
//custom binding to initialize a jQuery UI dialog
ko.bindingHandlers.jqDialog = {
init: function(element) {
ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
$(element).dialog("destroy");
});
},
update: function(element, valueAccessor) {
var options = ko.toJS(valueAccessor());
if (options) {
$(element).dialog(options);
}
}
};
I added a computed observable to your sample just to handle the selectedProduct being null (could be done in-line).
http://jsfiddle.net/rniemeyer/Gt5Hw/