I am trying to create a knockout template for my text count tracker so that I can repeat this for each text area on the page I am creating. I have created two fiddles that demonstrate different approaches I have been taking: 1. http://jsfiddle.net/ajdisalvo/9W84x/3/ and 2. http://jsfiddle.net/ajdisalvo/ex2uJ/
In both, I am have created a bindinghandler that is a wrapper for the template that I am attempting to bind to.
In the first approach I create a separate viewModel, attach it to the existing main viewModel and it seems to behave until it attempts to update the value 'successes' in the main viewModel. At this point, it sets the value to [Object object]. Although, this fiddle seems to be really close to working, I am concerned that I could be creating a recursive loop that might be an inherent flaw to this approach.
(function (ko) {
ko.bindingHandlers.templateWrapper = {
init: function (element, valueAccessor, allBindingsAccessor, viewModel, context) {
var existingOptions = ko.utils.unwrapObservable(valueAccessor());
viewModel[existingOptions.itemName] = new subModel(existingOptions.dataInput(), existingOptions.maxLength);
viewModel[existingOptions.itemName].itemText.subscribe(function (value) {
existingOptions.dataInput(value);
});
newOptions = ko.bindingHandlers.templateWrapper.buildTemplateOptions(existingOptions, viewModel[existingOptions.itemName]);
//return ko.bindingHandlers.template.init(element, function () { return newOptions; }, allBindingsAccessor, viewModel, context);
return { controlsDescendantBindings: true };
},
'update': function (element, valueAccessor, allBindingsAccessor, viewModel, context) {
newOptions = ko.bindingHandlers.templateWrapper.buildTemplateOptions(valueAccessor(), viewModel[valueAccessor().itemName]);
ko.bindingHandlers.template.update(element, function () { return newOptions; }, allBindingsAccessor, viewModel, context);
},
//extend the existing options by adding a name
buildTemplateOptions: function (options, vm) {
return { data: vm, name: ko.bindingHandlers.templateWrapper.templateName };
},
templateName: "textTrackerTemplate"
};
var viewModel = {
successes: ko.observable("Test input")
};
var subModel = function (value, maxLength) {
var self = this;
self.itemText = ko.observable(value);
self.maxLength = ko.observable(maxLength);
self.currentLength = ko.observable(self.itemText().length);
self.remainingLength = ko.computed(function () { return self.maxLength() - self.currentLength() });
self.hasFocus = ko.observable(false);
self.updateRemaining = function (data, event) {
var e = $(event.target || event.srcElement);
self.currentLength(e.val().length);
if (self.currentLength() > self.maxLength()) {
e.val(e.val().substr(0, self.maxLength()));
self.ItemText(e.val());
self.currentLength(self.itemText().length);
}
};
};
ko.applyBindings(viewModel);
} (ko));
In my second approach, I am using an extender in my bindinghandler to add the necessary properties to populate my count tracker, but it seems that the objects created in the extender are not instantiated at the time the knockout renders the page.
(function (ko) {
ko.bindingHandlers.templateWrapper = {
init: function (element, valueAccessor, allBindingsAccessor, viewModel, context) {
var existingOptions = ko.utils.unwrapObservable(valueAccessor());
newOptions = ko.bindingHandlers.templateWrapper.buildTemplateOptions(existingOptions, valueAccessor);
return ko.bindingHandlers.template.init(element, function () { return newOptions; }, allBindingsAccessor, viewModel, context);
},
'update': function (element, valueAccessor, allBindingsAccessor, viewModel, context) {
var existingOptions = ko.utils.unwrapObservable(valueAccessor());
newOptions = ko.bindingHandlers.templateWrapper.buildTemplateOptions(existingOptions, valueAccessor);
ko.bindingHandlers.template.update(element, function () { return newOptions; }, allBindingsAccessor, viewModel, context);
},
buildTemplateOptions: function (options, valueAccessor) {
valueAccessor().dataInput = valueAccessor().dataInput.extend({ textTracker: options.maxLength });
return { data: valueAccessor, name: ko.bindingHandlers.templateWrapper.templateName };
},
templateName: "textTrackerTemplate"
};
var viewModel = {
successes: ko.observable("Test input")
};
ko.extenders.textTracker = function (target, maxLength) {
target.itemText = ko.computed({
read: function () {
return target();
},
write: function (value) {
target(value);
}
});
target.maxLength = ko.observable(maxLength);
target.currentLength = ko.observable(target.itemText().length);
target.remainingLength = ko.computed(function () {
return target.maxLength() - target.currentLength(); });
target.hasFocus = ko.observable(false);
target.updateRemaining = function (data, event) {
var e = $(event.target || event.srcElement);
target.currentLength(e.val().length);
if (target.currentLength() > target.maxLength()) {
e.val(e.val().substr(0, target.maxLength()));
target(e.val());
target.currentLength(target.itemText().length);
}
};
return target;
};
ko.applyBindings(viewModel);
} (ko));
Thanks in advance for any help/suggestions that you might provide..