0

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..

4

1 回答 1

0

我想到了。我知道我的绑定处理程序存在问题。我实际上是让我的绑定变得更加复杂,这是它需要的。我只需要在 init 方法中扩展值访问器,然后将其传递给模板 init 绑定方法。在更新语句中,我只需要使用现有的值访问器,因为它已经被扩展了。这是新的绑定处理程序:

        ko.bindingHandlers.textTracker = {
            init: function (element, valueAccessor, allBindings, viewModel, context) {
                var options = ko.utils.unwrapObservable(allBindings());
                var observable = valueAccessor();
                var newValueAccessor = observable.extend({ textTracker: options });
                return ko.bindingHandlers.template.init(element,
                        function () {
                            return { data: newValueAccessor,
                                name: ko.bindingHandlers.textTracker.templateName
                            };
                        },
                        allBindings, viewModel, context);
            },
            update: function (element, valueAccessor, allBindings, viewModel, context) {
                return ko.bindingHandlers.template.update(element,
                        function () {
                            return { data: valueAccessor,
                                name: ko.bindingHandlers.textTracker.templateName
                            };
                        },
                        allBindings, viewModel, context);
            },
            templateName: "textTrackerTemplate"
        };

特别感谢 RPNiemeyer 对19732545回答,它帮助我重新审视了我正在做的事情,这有助于简化问题。

于 2013-11-05T14:02:44.183 回答