In my KncokoutJS ViewModel, I have the follow computed property:
self.SelectedUserHasRoles = ko.computed(function () {
if (self.isLoaded()) {
return self.selectedUser().roles().length > 0;
}
return false;
});
And in my HTML, I have the following:
<!-- ko if: isLoaded() -->
<!-- ko if: !SelectedUserHasRoles -->
<div>
<p>User has no Roles.</p>
</div>
<!-- /ko -->
<!-- ko if: SelectedUserHasRoles -->
<div class="roles-wrapper" data-bind="foreach: $root.selectedUser().roles()">
<div class="role-token" data-bind="text: Name"></div>
</div>
<!-- /ko -->
<!-- /ko -->
In my code, I was to say this:
If data from AJAX call has finished loading (isLoaded is true), then for the currently selected user, check and see if he/she has any roles. If yes, then loop through them and show them, if not, show a bit of text saying 'User has no Roles.'
All seems to work, except for the showing User has no Roles
text snippet. I've no idea why that isn't showing! I'm putting breakpoints into my computed property and can see that when I select a user with no roles, the expression (in console window) is false, and I'm negating that, so I should see that text snippet!
What am I doing wrong? I've created a screencast to make things easier to understand.
Because you are not binding to a variable but to an expression, you need to add parenthesis here:
See the following snippet
See user3297291's answer for more details.
You have the same check for isLoaded() twice, actually
if isLoaded() evalutes to false, your SelectedUserHasRoles() won't even be evaluated.
When you want to negate an observable or computed value in a binding, you have to call it explicitly:
In the case of the
if
binding, there's also theifnot
counterpart:I think it's useful to understand why this is needed, since I see it happening a lot.
You could see the data-bind attribute as a comma separated string of key value pairs. Knockout wraps each of the values in a function, which it calls the
valueAccessor
.Essentially, you'll go from:
to
SelectedUserHasRoles
is an observable instance, which evaluates as truthy. When you negate this value using an!
, it will always befalse
.The valueAccessor function is passed to the
init
method of a binding. Usually, it is retrieved by calling it, and then unwrapped. Because theunwrap
utility doesn't care about whether you pass it anobservable
or a plain value, you'll not see any errors when you make this mistake.I hope this small peek under the hood might help you (and others that have made this mistake) to be able to determine when the
()
are needed in the future.