When to use ko.utils.unwrapObservable?

2020-01-27 12:21发布

问题:

I've written a few custom bindings using KnockoutJS. I'm still unsure when to use ko.utils.unwrapObservable(item) Looking at the code, that call basically checks to see if item is an observable. If it is, return the value(), if it's not, just return the value. Looking at the section on Knockout about creating custom bindings, they have the following syntax:

var value = valueAccessor(), allBindings = allBindingsAccessor();
var valueUnwrapped = ko.utils.unwrapObservable(value);

In this case, they invoke the observable via () but then also call ko.utils.unwrapObservable. I'm just trying to get a handle on when to use one vs. the other or if I should just always follow the above pattern and use both.

回答1:

You should use ko.utils.unwrapObservable in cases where you don't know if you have been given an observable or not. This would commonly be in a custom binding where an observable or non-observable could be bound against it.

In the code that you have above, the call to valueAccessor() is not actually unwrapping an observable. It is just retrieving the value that was passed to the binding in the correct context (it gets wrapped in a function to protect it). The return value of valueAccessor() may be an observable or not. It is whatever was passed to the binding.



回答2:

The earlier answer is correct, but often I pass in functions to custom bindings (a function that checks permissions, or determines what to do based on something else, etc). What I really needed was to unwrap any function, even if it's not an observable.

The following recursively unwraps EVERYTHING:

ko.utils.unwrapFunction = function (func) {
    if (typeof func != 'function') {
        return func;
    }
    else {
        return ko.utils.unwrapFunction(func());
    }
};

Here is an example of a simple custom binding I wrote:

//replaces single and double 'smart' quotes users commonly paste in from word into textareas and textboxes with normal text equivalents
//USAGE:
//data-bind="replaceWordChars:true
//also works with valueUpdate:'keyup' if you want"

ko.bindingHandlers.replaceWordChars = {
    update: function (element, valueAccessor, allBindingsAccessor, viewModel) {
        var bindingValue = ko.utils.unwrapFunction(valueAccessor);

        if (bindingValue) {
            $(element).val(removeMSWordChars(allBindingsAccessor().value())); //update DOM - not sure why I should need to do this, but just updating viewModel doesn't always update DOM correctly for me
            allBindingsAccessor().value($(element).val()); //update viewModel
        }
    }
}

This way bindingValue always contains a value. I don't need to worry about if I passed in a function, an observable, a value, or a even a function inside an observable. This will properly unwrap everything until it gets at the object I want.

Hope that helps someone.