markaaronky
markaaronky

Reputation: 1305

Knockout unable to use function instead of ko.computed()

I have a UI that displays a knockout-bound checkbox and an HTML table. Depending on the setting of the checkbox and the values of certain properties of records in each row, I want hide certain rows.

I approached the problem by using knockout's visible binding on the TR:

<tr data-bind="visible: $root.showRowBasedOnFulfillmentCriteria">

and of course I have a checkbox, isOnlyFufillmentChecked:

<input type="checkbox" data-bind="checked: $root.isOnlyFulfillmentChecked, click: $root.productsRefresh" />

I first thought to approach this by making showRowBasedOnFulfillmentCriteria() a ko.computed, but there is a problem. The value of the checkbox (an observable), is only one of the criteria needed to determine row visibility. The other criteria change as I build the table, looping through records.

My understanding is that you cannot pass parameters to ko.computeds. Researching the problem on StackOverflow, everyone seems to be saying that I can easily use a function, placed inside my view model, instead of a computed. However, a function isn't working.

When I say a function isn't working, I mean I have stripped this to the most base, verifiable example. Here, from my view model, is showRowBasedOnFulfillmentCriteria() written as a ko.computed:

       self.showRowBasedOnFulfillmentCriteria = ko.computed(function () {
       return false;   // false will hide the rows, true will show them.  the computed() is "known" to the markup
   }, this);

As you can see, I took a very simple approach, just returning a boolean, to see if I can control the visibility of rows from the computed. Returning false hides the rows; return true shows them. BUT HERE'S WHAT HAPPENS WHEN i TRY IT AS A FUNCTION:

       self.showRowBasedOnFulfillmentCriteria = function () {
       return false;    
    };

Here, written as a function, even though always returning false, all of the rows display. In other words, the return value of the function doesn't seem to have any bearing. Before I can flesh out the real code of the function, I need to know I can use it to replace a ko.computed.

[EDIT: Using Chrome's debugger, setting a breakpoint in the function, it appears the function is never even called; a value of TRUE always seems to be assumed by the markup when I bind the FUNCTION to the visible property in knockout. Please read on...]

Have I misunderstood the claim that a ko.computed can easily be replaced with a function?

The function is defined in the same viewmodel as the observable bound to the checkbox and (when not commented out), the ko.computed version that can stand in for the function and does work.

If I haven't already written too much, my end goal for the function is this:

self.showRowBasedOnFulfillmentCriteria = function (flag1, flag2) {
       return self.isOnlyFufillmentChecked && flag1 && flag2;    
    };

And the TR binding would look like this:

<tr data-bind="visible: $root.showRowBasedOnFulfillmentCriteria(Criteria1, Criteria2)">

Upvotes: 0

Views: 421

Answers (1)

Brother Woodrow
Brother Woodrow

Reputation: 6372

You absolutely can do this. Just create a function on your view model and pass its output to the visible binding. You can pass in the current row as a parameter, as well as reference properties on your viewmodel.

Example code:

function ViewModel() {
  var vm = this;

  vm.hideNoFooRows = ko.observable(false);
  vm.row = ko.observableArray([
    { foo: true, val: 'Row 1' },
    { foo: false, val: 'Row 2' },
    { foo: true, val: 'Row 3' },
    { foo: false, val: 'Row 4' },
    { foo: true, val: 'Row 5' }
  ]);

  vm.isVisible = isVisible;

  function isVisible(row) {
    let hide = ko.unwrap(vm.hideNoFooRows);
    return hide ? row.foo : !hide;
  }
}

ko.applyBindings(new ViewModel());

HTML:

<table border="1" style="width: 50%">
  <tbody data-bind="foreach: row">
    <tr data-bind="visible: $parent.isVisible($data)">
      <td data-bind="text: val"></td>
      <td data-bind="text: foo"></td>
    </tr>
  </tbody>
</table>

JSFiddle: https://jsfiddle.net/thebluenile/cjmd1hp0/

Upvotes: 1

Related Questions