Search code examples
model-view-controllerrazorknockout.js

Pushing array to select in knockout gives just one option


I am using MVC and a Razor View I'm trying to bound data received from a controller to a select using a knockout model, If I try to push directly the dynamic array I get only one option like this one

Only one option select:

Only one option select

I'm sure that I'm missing something stupid, I have already tried to return a new SelectList and using optionsText and optionsValue but didn't do the work. I'm sure the knockout model is correct because if I write

viewModel.dliveryDates.push("option1","option2");

it works as expected

Here's my controller code that reads some data from database and send it back to the view

[HttpPost]
public JsonResult GetDeliveryDates(string code)
{
    OrderHeaderPageModel instance = ObjectFactory.Create<OrderHeaderPageModel>();
    instance.DeliveryDateRanges = PopulateDeliveryDateRanges(code);
    return Json(instance.DeliveryDateRanges.ToArray());
}

Here's is my View code

@Html.DropDownList("deliveryranges", new SelectList(string.Empty, "Code", "Description"), "- Seleziona -", new { @data_bind = "options:dliveryDates" })

And finally my knockout model

function OrderHeaderViewModel() {
    var self = this;
    self.save = function () {
        return true;
    }
    self.dliveryDates = ko.observableArray([]);
}

var viewModel = new OrderHeaderViewModel();
ko.applyBindings(viewModel, el);

$("#ordertypes").change(function () {
    var postUrl = "/checkout/getdeliverydates";
    $("#deliveryranges").empty();
    $.post(postUrl,
        {
            code: $("#ordertypes").val(),
            __RequestVerificationToken: Sana.Utils.getAntiForgeryToken()
        }, function (data) {
            var arry = [];
            var array = $.map(data, function (value, index) {
                return [value];
            });
            $.each(data, function (i, data) {
                arry.push(data.Code);
            });  
            viewModel.dliveryDates.push(arry);
        }
    );
})

Solution

  • It looks like the code is doing some extra work mapping data that is not used in the ajax callback. Hope the following code helps.

    function OrderHeaderViewModel() {
      var self = this;
      self.getData = function() {
    
        //function to simulate filling the array from the server.
        var data = ["Item 1", "Item 2", "Item 3", "Item 4"];
        self.dliveryDates(data);
    
        var mappedData = data.map(function(item, index) {
          return {
            id: index,
            description: item
    
          };
        });
        viewModel.mappedDliveryDates(mappedData);
    
      }
      self.save = function() {
        return true;
      }
      //added to put the selected values in
      self.selectedValue = ko.observable();
      self.selectedMappedValue = ko.observable();
    
      self.mappedDliveryDates = ko.observableArray([]);
      self.dliveryDates = ko.observableArray([]);
    
    }
    
    var viewModel = new OrderHeaderViewModel();
    ko.applyBindings(viewModel);
    
    $("#ordertypes").change(function() {
      var postUrl = "/checkout/getdeliverydates";
      $("#deliveryranges").empty();
      $.post(postUrl, {
        code: $("#ordertypes").val(),
        __RequestVerificationToken: Sana.Utils.getAntiForgeryToken()
      }, function(data) {
        // if the data needs to be transformed and is already an array then you can use
        var mappedData = data.map(function(item, index) {
          return {
            id: index,
            description: item
          };
        });
        // If the data is already in the format that you need then just put it into the observable array;
        viewModel.mappedDliveryDates(mappedData);
        viewModel.dliveryDates(data);
      });
    })
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
    
    Server Data Values -
    <select data-bind="options: dliveryDates, value:selectedValue, optionCaption: 'Choose...'"></select>
    <br/> Mapped Values -
    <select data-bind="options: mappedDliveryDates, optionsText:'description', value: selectedMappedValue, optionCaption: 'Choose...'"></select>
    <br/>
    <button data-bind="click: getData">Load Data</button>
    <br/>
    <br/>
    <pre data-bind="text: ko.toJSON($root)"></pre>