In my KncokoutJS ViewModel, I have the follow computed property:
self.SelectedUserHasRoles = ko.computed(function () {
if (self.isLoaded()) {
return self.s
You have the same check for isLoaded() twice, actually
<!-- ko if: isLoaded() -->
<!-- ko if: !SelectedUserHasRoles -->
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:
<!-- ko if: !SelectedUserHasRoles() -->
In the case of the if
binding, there's also the ifnot
counterpart:
<!-- ko ifnot: SelectedUserHasRoles -->
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:
data-bind="if: SelectedUserHasRoles"
to
{
"if": function() { return SelectedUserHasRoles }
}
SelectedUserHasRoles
is an observable instance, which evaluates as truthy. When you negate this value using an !
, it will always be false
.
var myObs = ko.observable("anything");
var valueAccessor = function() { return myObs; };
var valueAccessorNeg = function() { return !myObs; };
console.log(valueAccessor()); // Returns the observable
console.log(valueAccessorNeg()); // Always prints false
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
The valueAccessor function is passed to the init
method of a binding. Usually, it is retrieved by calling it, and then unwrapped. Because the unwrap
utility doesn't care about whether you pass it an observable
or a plain value, you'll not see any errors when you make this mistake.
var myObs = ko.observable(false);
var va1 = function() { return myObs; };
var va2 = function() { return !myObs; };
var va3 = function() { return !myObs(); };
console.log(ko.unwrap(va1())); // false
console.log(ko.unwrap(va2())); // always false
console.log(ko.unwrap(va3())); // true
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
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.
Because you are not binding to a variable but to an expression, you need to add parenthesis here:
<!-- ko if: !SelectedUserHasRoles() -->
//^^ here
See the following snippet
function CreateVM() {
var self = this;
this.isTrue = ko.observable(false);
this.selectedUser = ko.observable();
this.isLoaded = ko.observable();
self.SelectedUserHasRoles = ko.computed(function () {
if (self.isLoaded()) {
return self.selectedUser().roles().length > 0;
}
return false;
});
}
var vm = new CreateVM();
ko.applyBindings(vm);
var userWithRoles = { roles: ko.observableArray([1,2]) };
var userWithoutRoles = { roles: ko.observableArray([]) };
vm.selectedUser(userWithoutRoles);
vm.isLoaded(true);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<!-- 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: $data"></div>
</div>
<!-- /ko -->
SelectedUserHasRoles: <span class="role-token" data-bind="text: SelectedUserHasRoles"></span>
<!-- /ko -->
See user3297291's answer for more details.