0

I have an issue where I have an underlying observable array, which is sorted and exposed via a computed observable. Although I now have an issue where if I throttle the computed observable it seems to cause an issue when calling removeAll on the underlying array.

The scenario is quite convoluted but basically I have a set of about 0-200 rows in the buffer section which is the observable array, then I display sections of that array dependant upon which tab the user has selected. So out of 100 records I may only filter it down to 30 in the computed observable. There are points though where I need to invalidate the array and download a new list, which may come down individually or in batches so it is throttled to reduce un-needed re-evaluations. However when I try to invalidate the underlying array with removeAll it seems to notify the view that stuff has changed and it tries to re-evaluate view level bindings, however some of them look at the underlying array which is now empty, but as the computed has not caught up it falls over.

So is there a way to force an evaluation on the computed after the removeAll on the array?

Here is an example of what I mean:

var currentFilterType = ko.observable(1); // this is changed in the UI by the user
var underlyingArray = ko.observableArray();
var filteredDetails = ko.computedObservable(filterPredicate);

function filterPredicate() {
   // assume ko.linq is included, this is a simple version of whats happening
   return underlyingArray.Where(function(x){ return x.FilterType() == currentFilterType; })
                         .OrderBy(function(x){ return x.DateCreated(); })
                         .ToObservableArray();
}

function invalidateData() {
   underlyingArray.removeAll();
   // fetch some more data to repopulate array
}

function doSomethingWithItem(data) {
   // check something against the original array
}

// In the view usage would look like this
<!-- ko foreach: filteredDetails -->
   <a data-bind="click: doSomethingWithItem"></a> 
<!-- /ko -->
Grofit
  • 17,693
  • 24
  • 96
  • 176
  • Some code would probably help. But without seeing any code at all, perhaps another observable could be used at the beginning of your computed observable to control execution of the rest of the computed. – Matt Burland Feb 03 '14 at 20:17
  • It is a long contrived scenario, I can show the basics however it is very difficult to show every part of it. Will edit the question to better display the issue. – Grofit Feb 03 '14 at 20:42
  • Why are you doing a removeAll? When you fetch the data from the server again, just assign it to the original array. Knockout would take care of the rest. – CtrlDot Feb 03 '14 at 21:25
  • Can you flesh out the filterPredicate function – Robert Slaney Feb 04 '14 at 01:57
  • @CtrlDot The reason is because not every server update required the set be invalidated. It is a bit like a chat room in a way, so every time a new message comes in it is appended to the list. So it will not want to clear down at this point. However if the user wants to go to a previous page of chat they would need to clear down their current set of data and then load in the new set. So not every server request results in an `removeAll()` – Grofit Feb 04 '14 at 08:45

1 Answers1

0

I think the problem is with the shouldBeVisible function. If that reads the underlyingArray observable then that visible binding will take a dependency on that observable. When you call removeAll, the visible binding will update.

If you throttle the computed, filteredDetails will delay the recomputation, but shouldBeVisible will be re-evaulated immediately.

The shouldBeVisible function takes a data instance, but nothing is passed when the visible binding executes the function. This function should also NOT access anything outside of the data passed.

Start by changing the visible binding passing in the current item from filteredDetails, then evaluate the condition. If you need to use the underlyingArray observable, use .peek() to avoid taking a dependency. filteredDetails will eventually recomputed.

<!-- ko foreach: filteredDetails -->
   <div data-bind="visible: shouldBeVisible($data)"></div> 
<!-- /ko -->
Robert Slaney
  • 3,712
  • 1
  • 21
  • 25
  • Sorry the coding error is my fault, it is actually a click handler, which automatically gets passed the current item in the itteration, will update the code to reflect this. – Grofit Feb 04 '14 at 08:42