knockout.js viewModel not displaying retrieved json from controller

63 views Asked by At

Im using a $.getJson to retrieve json objects into my viewmodel and applybindings. It will not display the databindings in the foreach come what may. [code] - index.cshtml

@{
    ViewData["Title"] = "Home Page";
}
@section Scripts {
    <script>
        require(['knockout', 'jquery', 'bootstrap', 'components/clients-list.component'], function (ko) {
            ko.applyBindings();
        });
    </script>
}
<!DOCTYPE html>

<head>
    <title>Index</title>
</head>
<body>
    <div class="text-center">
        <h1 class="display-4">Welcome</h1>
        <p>Machine Grid</p>
        <div>
            <table>
                <tbody data-bind="foreach: machines">
                    <tr style="border-bottom: 1px solid #000000;">
                        <td>
                            <span data-bind="text: machineid"></span>
                        </td>
                        <td>
                            <span data-bind="text: ports"></span>
                        </td>
                        <td>
                            <span data-bind="value: Weight"></span>
                        </td>
                    </tr>
                </tbody>
            </table>
        </div>
    </div>
    <div>

    </div>
</body>


<script type="text/javascript">

    var AppViewModel = function () {
        var self = this;
        self.machines = ko.mapping.fromJS([]);
        $.getJSON('/Home/Index/', function (data) {
            self.machines = ko.mapping.fromJS(data);
        });
    };

    $(document).ready(function () {
        var viewModel = new AppViewModel();
        ko.applyBindings(viewModel);
    });
    
</script>
//Models.MachineModel
 public class MachineModel
    {
        public int MachineId { get; set; }

        public string HardDrive { get; set; }

        public string Ports { get; set; }

        public string GraphicsCard { get; set; }

        public string Weight { get; set; }

        public string Power { get; set; }

        public string Processor { get; set; }

        public string Ram { get; set; }
    }

//Controller


        public async Task<IActionResult> Index()
        {

            IEnumerable<MachineModel> machineList;
                using (var httpClient = new HttpClient())
                {
                    using (var response = await httpClient.GetAsync("https://localhost:44336/api/Machine"))
                    {
                        string apiResponse = await response.Content.ReadAsStringAsync();
                        machineList = JsonConvert.DeserializeObject<IEnumerable<MachineModel>>(apiResponse);
                    }
                }
            return View(machineList);
            
        }

There is a a populated object I have checked when debugging. I have also tried using Json(machineList) but that comes up with a dialog box to download json and then terminates.

2

There are 2 answers

0
ORed On

public async Task Index() {

        return View();
       
    }

    public async Task<JsonResult> Machine()
    {
        IEnumerable<MachineModel> machineList;
        using (var httpClient = new HttpClient())
        {
            using (var response = await httpClient.GetAsync("https://localhost:44336/api/Machine"))
            {
                string apiResponse = await response.Content.ReadAsStringAsync();
                machineList = JsonConvert.DeserializeObject<IEnumerable<MachineModel>>(apiResponse);
            }
        }
        return Json(machineList);
    }
1
Nathan Fisher On

A couple of things I can see,

  • Object property name casing matters, so check the casing on the data being returned from server and make sure that the data-binds match.

  • as a general rule, only use the data-bind="value: someProperty" for elements that you can enter data into. usually this the <input /> element.

  • check the browser console (F12) for binding errors as this is a big help for discovering where the errors are. enter image description here

  • If you get really stuck, add <pre data-bind="text: ko.toJSON($root)"></pre> to the html to help get a view of the state of the data in your view-model

var data = [{
  "machineId": 1,
  "hardDrive": "2TB",
  "ports": "USB Type-C",
  "graphicsCard": "3080",
  "weight": "5.3 kg",
  "power": "650w",
  "processor": "Intel I7",
  "ram": "32GB"
}, {
  "machineId": 2,
  "hardDrive": "2TB",
  "ports": "USB Type-C x2",
  "graphicsCard": "3080",
  "weight": "5.3 kg",
  "power": "650w",
  "processor": "Intel I7",
  "ram": "32GB"
}]


var AppViewModel = function() {
  var self = this;
  self.machines = ko.mapping.fromJS(data);
};

var viewModel = new AppViewModel();
ko.applyBindings(viewModel);
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.6.0/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout.mapping/2.4.1/knockout.mapping.min.js"></script>

<pre data-bind="text: ko.toJSON($root)"></pre>
<div class="text-center">
  <h1 class="display-4">Welcome</h1>
  <p>Machine Grid</p>
  <div>
    <table class='table'>
      <thead>
        <tr>
          <th>Machine Id</th>
          <th>Ports</th>
          <th>Weight</th>
        </tr>
      </thead>
      <tbody data-bind="foreach: machines">
        <tr style="border-bottom: 1px solid #000000;">
          <td>
            <span data-bind="text: machineId"></span>
          </td>
          <td>
            <span data-bind="text: ports"></span>
          </td>
          <td>
            <span data-bind="text: weight"></span>
          </td>
        </tr>
      </tbody>
    </table>
  </div>
</div>