Cliff Chambers
Cliff Chambers

Reputation: 73

Knockout JS ObservableArray losing values

I am new to Knockout. I'm successfully pulling data, but trying to filter it based on whether the record is active or not active. When I do this, it's sending the right values through, but my observable array is empty.

I'm filling it with ajax. My understanding is that I fill the observable array, and do not need to do any other hits to the server unless I want to write to the database or get new data. So once that observable array has data, I should be able to filter it without hitting the database again. This doesn't seem to work for me. I think I'm doing something wrong. Thoughts?

Here is the code:

var sgsoip = window.sgsoip || {};
sgsoip.FunctionalAreaViewModel = function (ko, db) {
    //'use strict';
    var self = this;
    self.functionalAreas = ko.observableArray([])

    self.headers = [
        { title: '', sortPropertyName: '', asc: true },
        { title: 'Functional Area Name', sortPropertyName: 'FunctionalAreaName', asc: true },
        { title: 'Active', sortPropertyName: 'FunctionalAreaActive', asc: true }
    ];
    self.filters = [
        { title: "Show All", filter: null },
        { title: "Active", filter: function (item) { return item.FunctionalAreaActive == 'true'; } },
        { title: "Inactive", filter: function (item) { return item.FunctionalAreaActive == 'false'; } }
    ];

    self.activeFilter = ko.observable(self.filters[0].filter);

    self.setActiveFilter = function (model, event) {
        self.activeFilter(model.filter);
    }

    self.filteredItems = ko.computed(function () {
        if (self.activeFilter()) {
            return ko.utils.arrayFilter(self.functionalAreas, self.activeFilter()); //When hitting filter self.functionalAreas is empty
        } else {
            return self.functionalAreas(); //this works as expected
        }
    });

    self.activeSort = self.headers[1];
    function _init() {
        db.getFunctionalAreas(function (data) {
            //var a = [];
            ko.utils.arrayForEach(data || [], function (item) {
                self.functionalAreas.push(new sgsoip.FunctionalArea(item.FunctionalAreaID, item.FunctionalAreaName, item.FunctionalAreaActive));
            });
            //self.functionalAreas(a);
        });
    }
    
    _init();
    return {
        functionalAreas: functionalAreas,
        sortedFunctionalAreas: sortedFunctionalAreas,
        removeFunctionalArea: removeFunctionalArea
    };
}(ko, sgsoip.DataContext);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<div class="btn-group" data-bind="foreach: filters">
        <button class="btn btn-small" data-bind="click: setActiveFilter, text: title"></button>
    </div>

    <table class="table" id="gridoutput">
        <thead>
            <tr data-bind="foreach: headers">
                <th><a href="#" data-bind="click: filteredItems, text: title"></a></th>
            </tr>
        </thead>
        <tbody data-bind="foreach: filteredItems">
            <tr>
                <td><a href="javascript:void(0);" data-bind="click: removeFunctionalArea" title="Delete"><i class="icon icon-trash"></i></a></td>
                <td data-bind="text: FunctionalAreaName"></td>
                <td data-bind="text: FunctionalAreaActive"></td>
            </tr>
        </tbody>
    </table>

Edit - Added some missing stuff for those that asked.

var sgsoip = window.sgsoip || {};
sgsoip.DataContext = (function ($) {
    'use strict';
    var me = {
        getFunctionalAreas: getFunctionalAreas,
        removeFunctionalArea: removeFunctionalArea,
        saveFunctionalArea: saveFunctionalArea
    };
    function getFunctionalAreas(callback) {
        var functionalareas = null;
        if ($.isFunction(callback)) {
            //functionalareas = localStorage["functionalareas"];
            //alert('test');

            //if (functionalareas != "undefined") {
            //    callback(functionalareas);
            //} else {
            $.getJSON('/FunctionalAreas/GetJsonData', function (data) {
                    //localStorage["functionalareas"] = JSON.stringify(data.FunctionalAreas);
                callback(data);
                });
            //}
        }
    }

    function removeFunctionalArea(functionalArea) {

    }

    function saveFunctionalArea(functionalArea) {

    }
    return me;
})(jQuery);


var sgsoip = window.sgsoip || {};
sgsoip.FunctionalArea = function (FunctionalAreaID, FunctionalAreaName, FunctionalAreaActive) {
    'use strict';
    this.FunctionalAreaID = ko.observable(FunctionalAreaID);
    this.FunctionalAreaName = ko.observable(FunctionalAreaName).extend({ required: "Functional Area Name is required" });
    this.FunctionalAreaActive = ko.observable(FunctionalAreaActive).extend({ required: "Active is required" });
    this.HasError = ko.pureComputed(function () {
        return this.FunctionalAreaActive.hasError() || this.FunctionalAreaName.hasError();
    }, this);
};

EDIT Looks like I can add more clarification. It is not losing data. I stepped all the way through the Knockout code. And it does cycle through the observable to compare.

I get to the following piece of code:

arrayFilter: function (array, predicate) {
            array = array || [];
            var result = [];
            for (var i = 0, j = array.length; i < j; i++)
                if (predicate(array[i], i))
                    result.push(array[i]);
            return result;
        },

And it never executes the result.push(array[i]) line.

Upvotes: 0

Views: 773

Answers (1)

Steen T&#248;ttrup
Steen T&#248;ttrup

Reputation: 3835

When iterating or in any other way working with your observable array, you need to remember to add the parenthesis at the end, like so:

   return ko.utils.arrayFilter(self.functionalAreas(), self.activeFilter());

It's only when using methods that are actually implemented on the observableArray (like push, remove etc), that you can leave out the parenthesis.

Upvotes: 1

Related Questions