When to use or not use parentheses with observables in data-binding expressions

…衆ロ難τιáo~ 提交于 2019-12-04 15:51:31
  1. 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.

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

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

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