1
votes

Edit: Added code for function populateDropdown and function isSystemCorrect (see bottom)

Edit 2 I have narrowed it down a bit and the problem seems to arise in the arrayFilter function in the computed observable. This returns an empty array, no matter what I try. I have checked that self.testsuites() looks ok right before filtering, but the filtering still fails.

I have a problem with my computed observable, filteredTestsuites.

As you can see from the screendump, the testsuites observable is populated correctly, but the computed observable remains empty. I have also tried choosing another option than "Payment" from the dropdown menu, to see if this will trigger the observable, it did not.

I would think the computed observable would be updated every time self.testsuites() or self.dropdownSelected() was changed, but it doesnt seem to trigger on neither of them.

What am I doing wrong here?

I simply want to make the computed observable filter the testsuites after the chosen dropdown option, every time either of them change.

Screendump from console

function ViewModel() {
    var self = this;

    // The item currently selected from a dropdown menu
    self.dropdownSelected = ko.observable("Payment");

    // This will contain all testsuites from all dropdown options
    self.testsuites = ko.mapping.fromJS('');

    // This will contain only testsuites from the chosen dropdown option
    self.filteredTestsuites = ko.computed(function () {
        return ko.utils.arrayFilter(self.testsuites(), function (testsuite) {
            return (isSystemCorrect(testsuite.System(), self.dropdownSelected()));
        });
    }, self);

    // Function for populating the testsuites observableArray
    self.cacheTestsuites = function (data) {
        self.testsuites(ko.mapping.fromJS(data));
    };


    self.populateDropdown = function(testsuiteArray) {

        for (var i = 0, len = testsuiteArray().length; i < len; ++i) {

            var firstNodeInSystem = testsuiteArray()[i].System().split("/")[0];

            var allreadyExists = ko.utils.arrayFirst(self.dropdownOptions(), function(option) {
                return (option.Name === firstNodeInSystem);
            });

            if (!allreadyExists) {
                self.dropdownOptions.push({ Name: firstNodeInSystem });
            }
        }
    };
}


$(document).ready(function () {

    $.getJSON("/api/TestSuites", function (data) {
        vm.cacheTestsuites(data);
        vm.populateDropdown(vm.testsuites());
        ko.applyBindings(vm);
    });
}

Function isSystemCorrect:

function isSystemCorrect(system, partialSystem) {

    // Check if partialSystem is contained within system. Must be at beginning of system and go
    // on to the end or until a "/" character.
    return ((system.indexOf(partialSystem) == 0) && (((system[partialSystem.length] == "/")) ||     (system[partialSystem.length] == null)));
}
1
Perhaps you should also show us populateDropdown and isSystemCorrect as it is most likely that the 'bug' occurs there. - Tyblitz
Added the functions :) - jonasjuss
First thing I would try is to call the observables with and without paranthesis. Like self.testsuites and self.testsuites(). I've seen lots of situations where changing the "call" to the observable to with or without parenthesis had fixed various silent errors. - Ole Viaud-Murat
How should the system and partialSystem arguments look like if we want a true result? - haim770
The cacheTestsuites call is wrapping the contents of testsuites causing the first parameter to arrayFilter to not be an array thereby causing your computed issues. Just before the filtering console.log(self.testsuites()) and you'll see the issue. - Origineil

1 Answers

2
votes

As suggested in a comment - rewrite the cacheTestsuites method:

self.testsuites = ko.observableArray();
self.filteredTestsuites = ko.computed(function () {
    return ko.utils.arrayFilter(self.testsuites(), function (testsuite) {            
        return (isSystemCorrect(testsuite.System(), self.dropdownSelected()));
    });
});
self.cacheTestsuites = function (data) {
    var a = ko.mapping.fromJS(data);
    self.testsuites(a());
};

The only thing different here is the unwrapping of the observableArray from the mapping function.