Reputation: 637
I'm having a string that contains many editable region, please consider this string
var str = "Regular ... are patterns used to match character .... in strings. In ..., regular expressions are also objects."
editable regions are contains ('...'), I want to make that places editable. means i want inject a editable content inside that string. I would like to use knockout for this purpose. I've referred the niemeyer's about content editable using knockout. can anyone suggest me a better way to achieve this. this my code here
ko.bindingHandlers.editableContent = {
init: function(element, valueAccessor, allBindingsAccessor) {
$(element).css({ "background-color": "#ff77ee", "margin-left": "-2px", "letter-spacing": "0.05"});
ko.utils.registerEventHandler(element, "keyup", function() {
var modelValue = valueAccessor();
var elementValue = element.innerHTML;
if (ko.isWriteableObservable(modelValue)) {
modelValue(elementValue);
}
else { //handle non-observable one-way binding
var allBindings = allBindingsAccessor();
if (allBindings['_ko_property_writers'] && allBindings['_ko_property_writers'].editableContent) allBindings['_ko_property_writers'].editableContent(elementValue);
}
})
},
update: function(element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor()) || "";
element.innerHTML = value;
}
};
ko.bindingHandlers.myBinding = {
init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
var div = document.createElement("div");
var fulltext = valueAccessor();
element.appendChild(div);
div.innerHTML = fulltext().replace(/\.\.\./g, "<span data-bind = 'editableContent : editedText' contenteditable='true'></span>");
ko.applyBindings(viewModel,div);
},
update: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
}
};
$(document).ready(function () {
var viewModel = function () {
var self = this;
self.fullText = ko.observable('Regular ... are patterns used to match character .... in strings. In ..., regular expressions are also objects.');
self.editedText = ko.observable('...');
}
ko.applyBindings(new viewModel());
});
Upvotes: 0
Views: 566
Reputation: 114792
The main issue is that each editableContent binding is bound against the same observable, so they are updating each other.
Here is a quick attempt at splitting the text into pieces and adding individual observables for each separate contenteditable.
ko.bindingHandlers.myBinding = {
init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
var div = document.createElement("div"),
fulltext = valueAccessor(),
split = fulltext().split(/\.\.\./g),
span, editable, editableSpan, pieces = [];
element.appendChild(div);
//loop through each pieces and add text and editable content
for (var i = 0, j = split.length; i < j; i++) {
//create a span for the fixed content
span = document.createElement("span");
span.innerHTML = split[i];
//keep track of each piece, so we can later put them back together to update original
pieces.push(split[i]);
div.appendChild(span);
//add editable content between the pieces, except at the end
if (i < split.length - 1) {
editable = ko.observable("...");
editableSpan = document.createElement("span");
pieces.push(editable);
div.appendChild(editableSpan);
ko.applyBindingsToNode(editableSpan, { editableContent: editable });
}
}
//update the original observable with the current value
ko.computed({
read: function() {
var result = "";
ko.utils.arrayForEach(pieces, function(piece) {
result += ko.utils.unwrapObservable(piece);
});
valueAccessor()(result);
},
disposeWhenNodeIsRemoved: element
});
return { controlsDescendantBindings: true };
}
};
Sample here: http://jsfiddle.net/rniemeyer/8CkLn/
Note that this handles updating the original observable based on edits, but would not handle updating the content editable blocks, if you updated the original observable in another way.
Upvotes: 2