ko.computed property to determine visibility not working

前端 未结 3 1961
死守一世寂寞
死守一世寂寞 2021-01-23 07:38

In my KncokoutJS ViewModel, I have the follow computed property:

self.SelectedUserHasRoles = ko.computed(function () {
  if (self.isLoaded()) {
    return self.s         


        
相关标签:
3条回答
  • 2021-01-23 08:19

    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.

    0 讨论(0)
  • 2021-01-23 08:20

    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.

    0 讨论(0)
  • 2021-01-23 08:34

    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.

    0 讨论(0)
提交回复
热议问题