How to access the observables in custom bindings when using Knockout-ES5

折月煮酒 提交于 2020-01-01 11:49:24

问题


If the model properties are ko.observable(), these can be accessed as below within the custom binding.

var observable = valueAccessor();

When using Knockout-ES5 plugin how to get hold of the observable within the custom binding? Check the code below and look for comment "How to get propertyName here?"

JS Fiddle when not using Knockout-ES plugin courtesy of Another Look at Custom Bindings for KnockoutJS

Updated fiddle with model changed to use Knockout-ES plugin

ko.bindingHandlers.datepicker = {
    init: function(element, valueAccessor, allBindingsAccessor, viewModel) {
        //initialize datepicker with some optional options
        var options = allBindingsAccessor().datepickerOptions || {};
        $(element).datepicker(options);

        //handle the field changing
        ko.utils.registerEventHandler(element, "change", function () {
            var observable = valueAccessor();
            if (!ko.isObservable(observable)) {                
                console.log("Not Observable");
                //How to get propertyName here?
                //ko.getObservable(viewModel, 'propertyName');
                return;
            }
            observable($(element).datepicker("getDate"));
        });

        //handle disposal (if KO removes by the template binding)
        ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
            $(element).datepicker("destroy");
        });

    },
    //update the control when the view model changes
    update: function(element, valueAccessor) {
        var value = ko.utils.unwrapObservable(valueAccessor()),
            current = $(element).datepicker("getDate");

        if (value - current !== 0) {
            $(element).datepicker("setDate", value);   
        }
    }
};

var viewModel = {
    myDate: new Date("11/01/2011"),
    setToCurrentDate: function() {
       this.myDate = new Date();   
    }
};

ko.track(viewModel);

ko.applyBindings(viewModel);

回答1:


You could pass the actual observable to the binding:

data-bind="datepicker: ko.getObservable($data, 'myDate') ..."

http://jsfiddle.net/xb6vR/1/

But that's ugly. Fortunately, Knockout does provide a way (undocumented) to write to a property value from a binding:

//handle the field changing
ko.utils.registerEventHandler(element, "change", function () {
    var writable = valueAccessor();
    if (!ko.isObservable(writable)) {                
        var propWriters = allBindingsAccessor()._ko_property_writers;
        if (propWriters && propWriters.datepicker) {
            writable = propWriters.datepicker;
        } else {
            return;
        }
    }
    writable($(element).datepicker("getDate"));
});

http://jsfiddle.net/xb6vR/3/




回答2:


I ended up using combination of passing a propertyname and falling back to _ko_property_writers. JS Fiddle.

HTML

<input data-bind="datepicker: myDate, datePropertyName: 'myDate',
       datepickerOptions: { minDate: new Date() }" />

Javascript

ko.bindingHandlers.datepicker = {
    init: function(element, valueAccessor, allBindingsAccessor, viewModel) {
        var _viewModel = viewModel;
        //initialize datepicker with some optional options
        var options = allBindingsAccessor().datepickerOptions || {};
        $(element).datepicker(options);

        //handle the field changing
        ko.utils.registerEventHandler(element, "change", function () {
            var observable = valueAccessor();
            if (!ko.isObservable(observable)) {                
                console.log("Not Observable");
                var datePropertyName = allBindingsAccessor().datePropertyName;

                if (datePropertyName) {
                    console.log("Using datePropertyName");
                    observable = ko.getObservable(_viewModel, datePropertyName);
                }
                else {
                    console.log("No datePropertyName, trying _ko_property_writers");
                    var propWriters = allBindingsAccessor()._ko_property_writers;

                    if (propWriters && propWriters.datepicker) {
                        observable = propWriters.datepicker;
                    } else {
                        console.log("No way to get observable");
                        return;
                    }
                }
            }
            observable($(element).datepicker("getDate"));
        });

        //handle disposal (if KO removes by the template binding)
        ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
            $(element).datepicker("destroy");
        });

    },
    //update the control when the view model changes
    update: function(element, valueAccessor) {
        var value = ko.utils.unwrapObservable(valueAccessor()),
            current = $(element).datepicker("getDate");

        if (value - current !== 0) {
            $(element).datepicker("setDate", value);   
        }
    }
};

var viewModel = {
    myDate: new Date("11/01/2011"),
    setToCurrentDate: function() {
       this.myDate = new Date();   
    }
};

ko.track(viewModel);

ko.applyBindings(viewModel);



回答3:


As an addition to the answer of Michael Best, if you are using knockoutjs 3.0 together with the es5 plugin, it is not possible to access the observable with allBindingsAccessor()._ko_property_writers; However I figured out it is possible to access it with the ko.getObservable function. So it should look like this:

ko.bindingHandlers.myCustomBinder = {
    init: function (element, valueAccessor, allBindingsAccessor, context) {
        var observable = ko.getObservable(context, 'observableName');
    }
}



回答4:


Well, you could use the preprocess to put the getObservable and use it as normal inside the binding handler.

preprocess: function (markup: string) {
        console.log("markup", markup);

        var lastDotIndex = markup.lastIndexOf('.');

        if (lastDotIndex > 0) {
            var obj = markup.substring(0, lastDotIndex);
            var property = markup.substring(lastDotIndex + 1);

            return 'ko.getObservable(' + obj + ', \'' + property + '\');';

        } else {
            return 'ko.getObservable($data, \'' + markup + '\')';
        }
    },



回答5:


function es5Preprocess(val){
    var alt = val.split('.');
    var tail = alt.pop();
    if(alt.length == 0) alt.push('$data');
    tail = 'ko.getObservable( ' + alt.join('.') + ', ' + '\'' + tail + '\'' + ' )';
    return tail + ' || ' + val;
};
window.ko.es5.customBinding = function(name, binding){
    binding.preprocess = es5Preprocess;
    window.ko.bindingHandlers[name] = binding;
};

And then

window.ko.es5.customBinding('contentEditable', {
    init: function(element, valueAccessor) {
        var val = valueAccessor();
        element.onblur = function() {
            val(element.textContent);
        };
    },
    update: function(element, valueAccessor) {
        element.textContent = valueAccessor()();
    }
});


来源:https://stackoverflow.com/questions/16994320/how-to-access-the-observables-in-custom-bindings-when-using-knockout-es5

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!