Search code examples
javascriptasp.netknockout.jsasp.net-web-api2knockout-3.0

Ensure Observable Array is populated before Filter runs


Problem: When My filter method is executing that is used to display an Html element, my observable array has not been populated yet by my web-api call resulting in nothing to filter from.

My Solution : If I place an alert right before my filter method execution, everything seems to be working. Length of my observable array is 0 right before my alert call whereas it has a value after my alert.

Question: How can I ensure my array is populated without placing an alert. This issue is occurring on multiple Pages, placing an alert before my Html is rendered makes everything works fine.

I have a simple Observable Array and a Filter Method on it. Part of my Knockout Code :

self.currentVendorSupport = ko.observable(new VendorContact());

//Populates Observable Array - allManufactures 
self.allManufacturers = ko.observableArray([]);
 $.getJSON(serviceRoot + '/api/Manufacturer', function (data) {  
    var mappedManufacturers = $.map(data, function (item) {
        return new Manufacturer(manID = item.manID, name = item.name);
    });
    self.allManufacturers(mappedManufacturers);
});

//Filters allManufacturers 
self.GetCurrentVendor = function () {
 alert('allManufacturerLength value again:' + allManufacturerLength);
 return ko.utils.arrayFirst(self.allManufacturers(), function (item) {
     return item.manID === self.currentVendorSupport().manID();
        });
}

It seems to be working. It is not working with arrayFilter though, is it because of return type difference between the two, wrong syntax or something else?

self.GetCurrentManufacturer = ko.computed(function () {
if (self.allManufacturers().length > 0) 
{ 
return ko.utils.arrayFilter(self.allManufacturers(), function (item) 
    { 
    return item.manufacturerID === 
           self.currentVendorSupport().manufacturerID() }); 
    } 
else return new Manufacturer(0, '...'); 
}, self); 

Html Code: 
<label class="control-label readOnlyLabel" data-bind="text: GetCurrentVendor().name"></label> 

Solution

  • You can simply make GetCurrentVendor a computedObservable instead so that you can conditionally show a value based on the observable array length. Since it is computed it would react on changes made to the array and update its value.

    You can even make it pureComputed so it is only ever activated/computed when called.

    For example the computedObservable currentVendor would show "..." when the array is empty and the filtered name when the array is populated.

    Computed:

    self.currentVendor = ko.computed(function () {
        if(this.allManufacturers().length > 0) {
            return ko.utils.arrayFirst(this.allManufacturers(), function (item) {
                return item.manID === this.currentVendorSupport().manID();
            }).name;
        } else {
            return '...'
        }
    }, this)
    

    HTML:

    <label class="control-label readOnlyLabel" data-bind="text: currentVendor"></label>