0

I have a collection with x amount of Dropdowns. I would like to fill out the dropdown with the list comming from the DB.

The HTML

<div class="input-col input-col-short">
    <select data-bind="options: $parent.insuranceCoverList(), optionsText: 'displayName', optionsValue: 'insuranceCoverId', value: selectedInsuranceCover"></select>
</div>

The variable "arr" is empty when I try to return it. If I return just the object with values then it works but not with the "arr" variable. And this is my insuranceCoverList()

self.insuranceCoverList = function () {
    var arr = [];
    // Get data from the DB
    GetInsuranceCover(self.InsuranceTypeID())
        .done(function (data) {
            $(data).each(function (i, v) {
                arr.push(new AvailableDropdownItems({ dropdownItemId: v.ProgramCode, dropdownItemName: v.DisplayValue }));
            });
        });
    // doesn't work 
    return arr;
    // works
    //return [{ insuranceCoverId: 0, displayName: "Option A" }, { insuranceCoverId: 1, displayName: "Option B" }, { insuranceCoverId: 2, displayName: "Option C" }];
};

Any suggestions where I fail?

loonybin
  • 65
  • 1
  • 14
  • Possible duplicate of [How do I return the response from an asynchronous call?](https://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call) – Serge K. Jun 22 '17 at 12:09

1 Answers1

2

Ajax is asynchronous. You can't return values from asynchronous functions.

This is the perfect situation for observables. Make an observable array that holds your list items and let the view depend on that array.

This way the view will get updated automatically as soon as you write items into this array, for example when an Ajax request returns. Exactly as Knockout was designed to be, exactly as it should be.

Viewmodel:

self.InsuranceTypeID = ko.observable();
self.insuranceCoverList = ko.observableArray();

// automatic loading!
self.InsuranceTypeID.subscribe(function (insuranceTypeId) {
    GetInsuranceCover(insuranceTypeId).done(function (data) {
        self.insuranceCoverList(data.map(function (v) {
            return new AvailableDropdownItems({
                dropdownItemId: v.ProgramCode,
                dropdownItemName: v.DisplayValue
            });
        }));
    });
});

View:

<div class="input-col input-col-short">
    <select data-bind="options: $parent.insuranceCoverList, optionsText: 'displayName', optionsValue: 'insuranceCoverId', value: selectedInsuranceCover"></select>
</div>

Generally you should not .push() to an observable array in a loop. Every change to the array notifies all of its dependencies (subscribers/view bindings), which in turn notify their dependencies, and so on.

If you are going to replace the contents of the array anyway, create the items first and then push them into the array in one step, like above where the array receives the result of data.map().

Tomalak
  • 332,285
  • 67
  • 532
  • 628
  • 1
    Thank you for helping me out and give me the tip with the `data.map()`. It finaly works the only thing I had to add was `self.InsuranceTypeID = ko.observable();` – loonybin Jun 22 '17 at 14:32
  • Standard JS function, [`Array#map`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/map). About `self.InsuranceTypeID()` - I thought that was already the case. You use it like an observable in your own code. – Tomalak Jun 22 '17 at 14:33