116

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.

Brian
  • 6,910
  • 8
  • 44
  • 82
arb
  • 7,753
  • 7
  • 31
  • 66

2 Answers2

147

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.

RP Niemeyer
  • 114,592
  • 18
  • 291
  • 211
  • So is the pattern I've posted best practice for custom bindings going forward? – arb Mar 08 '12 at 20:56
  • 6
    It really depends on the situation. Some custom bindings are designed to only work with observables, so you may check up front (ko.isObservable) that it is an observable and then you would be free to unwrap it with (). If you are receiving an object that may have nested observables, then you are better off doing a `ko.toJS(yourObject)` rather than using `ko.utils.unwrapObservable`, if you are trying to get an unwrapped version of the object to pass into a widget or 3rd party library. In general, it is safest to use `ko.utils.unwrapObservable` to support observables and non-observables. – RP Niemeyer Mar 08 '12 at 21:15
  • 2
    I guess I'm confused with what the purpose of `ko.utils.unwrapObservable` is. Looking at the code, it just checks to see if it's observable and if it is, Knockout invokes `()` to get the value of the observable, otherwise, it just returns the value for non-observable. If all I'm interested is the value of the data passed into the binding, why can't I just always use `()`? – arb Mar 08 '12 at 21:40
  • 18
    You don't know if you are getting passed an observable or a non-observable in a binding. Say I have `myBinding` and someone binds ie like `data-bind="myBinding: myValue"`. `myValue` might be an observable or it might just be a plain property on the view model. If it is just a property and I call it like a function, then you will get an error. `ko.utils.unwrapObservable` will safely return you the value regardless of whether you were passed an observable or not. – RP Niemeyer Mar 08 '12 at 22:10
  • 11
    I'd also recommend using 'ko.unwrap' shorthand because 'ko.utils.unwrapObservable' is a very long expressions. – Ivan Nikitin Mar 17 '14 at 12:46
  • 4
    @IvanNikitin - sure, just wanted to point out that `ko.unwrap` is available in 3.0+. If you are using older than 3.0, then `ko.utils.unwrapObservable` is still there. – RP Niemeyer Mar 17 '14 at 15:18
  • @RPNiemeyer your answer helped me a lot epecially the last comment `ko.utils.unwrapObservable` +1 – SpringLearner May 19 '14 at 13:24
14

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.

pilavdzice
  • 958
  • 8
  • 27