When ko parses the bindings, it checks if the expression is an observable, which, as you know is a function. If the expression is an observable, ko automatically unwraps the value to show it, but it also allows subscriptions and notifications.
In this case, when ko parses the expression, it finds a value, not an observable, so, it also works correctly to show the value (value changes > view updates). However, you'd lose the binding from the view to the value (input value changes > observable is not updated), because it's not an observable. For more details, see explanation and snippet below.
customProperty
is a function, in particular a ko.observable
, which means that supports subscriptions and notifications, apart form reading or setting the value using the ()
or (newValue)
syntax
NOTE: the difference between using and not using parentheses is huge. If you do this:
<input type="text" data-bind="value: customProperty" ...
as I explain in 1, ko finds that customProperty
is an observable, so when the user changes the value in the input
, the new value is written back to the observable. If you do this:
<input type="text" data-bind="value: customProperty()" ...
as I explain in 2, ko finds a value, not an observable. So, if the user changes the value of the input
by typing on it, the new value is not fed back to the observable, because ko doesn't know it's an observable. (But if the observable value is updated, the view changes, because the dependency is discovered and subscribed during the expression evaluation).
var vm = {
customProperty: ko.observable(10)
};
ko.applyBindings(vm);
body {
font-family: Segoe, Arial
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
data-bind="value: customProperty()"<br/>
<input type="text" data-bind="value: customProperty(), valueUpdate: 'keyup'"/><br/>
If you change the input text, customProperty is not updated
<br/><br/>
data-bind="value: customProperty"<br/>
<input type="text" data-bind="value: customProperty, valueUpdate: 'keyup'"/><br/>
If you change the input text, customProperty changes
<br/><br/>
customProperty value: <span data-bind="text: customProperty"/>
In other frameworks, like Angular, instead of using functions, it uses properties with JavaScript setters and getters so that the syntax never needs parentheses. Properties with settes and getters are read and written as any other property, but behind the scenes the code in the setter or getter runs, allowing subscriptions and notifications to happen.
NOTE 2 (because of question in the comment). You can think of it like this: when ko parses a binding expression, it evaluates the whole expression at once and checks if the result is an observable. So, when you have an expression like this: customProperty == 10
, when ko evaluates it, it finds it's not an observable (but a boolean) and takes no extra steps to get the value. The result will always be false, because customProperty
is a function
, and thus is '!= 10'. If you change the expression to this one: customProperty() == 10
the custom property value will be unwrapped by the ()
, and the comparison will work as expected. By the way, try not to include code in the binding expressions: it's much better to use computed observables (better pure computeds, if possible) in your model.
Console experiment for note 2
Type: var vm = {customProperty: ko.observable(10)}
to create a view model.
Type: vm.customProperty()
, and you'll see 10
as a result.
Type: vm.customProperty
, and you'll see function ...
as a result.
Type: vm.customProperty() == 10
, and you'll see true
(no wonder, 10 == 10
)
Type: vm.customProperty == 10
, and you'll get false
(because function != 10
)
Furthermore, type ko.isObservable(vm.customProperty)
and you'll see true
. That's what ko does. So ko knows it must unwrap the value. Type ko.unwrap(vm.customProperty)
and you'll see 10
Finally, type ko.isObservable(vm.customProperty == 10)
or ko.isObservable(vm.customProperty() == 10)
. In both cases you'll get false
, becase the expression is a bool
in both cases, and not an observable function. Ko doesn't decompse the expression and check it piece by piece. That would be the only way to discover in the first expression that customProperty
is an observable and should be unwrapped. But ko does not do it in that way.
NOTE 3: expressions, as computed observables, are re-evaluated when an observable property was used in the original evaluation and it changes its value. Be warned that if in the first evaluation you only access an observable property, even if the code contains references to other observables, it will only be re-evaluated when the accessed observable changes its value. Changes on the other observables won't be "observed". The typical case is an if
that depends on different observables depending on the executed branch