https://stackoverflow.com/questions/47158758/knockoutjs-how-to-properly-bind-third-level-mapping

by jlspake

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout.mapping/2.4.1/knockout.mapping.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.5/js/select2.min.js"></script>

<span data-bind="text: Title"></span>
<br/>
<span data-bind="text: LogoPath"></span>
<br/>
<span data-bind="text: StartDate"></span>
<br/>
<span data-bind="text: EndDate"></span>
<br/>
<div data-bind="foreach: ReservationOptions">
    <div  style="border: 1px dashed blue;">
        <span data-bind="text: Title"></span>
        <br/>
        <span data-bind="text: Info"></span>
        <br/>
        <span data-bind="text: TypeDescription"></span>
        <br/>
        <div data-bind="foreach: ReservationOptionValues">
            <div style="border: 1px dashed red;">
                <span data-bind="text: ValueTitle"></span>
            </div>
        </div>
    </div>
</div>

CSS

div {
  min-height: 100px;
  padding: 4px;
}

JavaScript

var data = {
  "Id": 1017,
  "Title": "title1",
  "LogoPath": "logo1",
  "StartDate": "06/11/2017",
  "EndDate": "06/11/2017",
  "StartTime": "00:00",
  "EndTime": "00:10",
  "TimeSpan": 1,
  "MinPersons": 2,
  "MaxPersons": 3,
  "CompanyId": 1,
  "ReservationOptions": [{
    "Id": 1011,
    "Title": "desc1",
    "Info": "info1",
    "TypeId": 1,
    "TypeDescription": "Radio Button",
    "ReservationOptionValues": [{
      "Id": 1034,
      "ValueTitle": "a"
    }, {
      "Id": 1035,
      "ValueTitle": "b"
    }, {
      "Id": 1036,
      "ValueTitle": "c"
    }],
    "NewValues": null
  }]
};

ReservationViewModel = function(data) {
  var self = this;
  ko.mapping.fromJS(data, reservationOptionsMapping, self);
}
// 2nd level constructor
ReservationOptionsViewModel = function(data) {
  var self = this;
  ko.mapping.fromJS(data, reservationOptionValuesMapping, self);
}
 //3nd level constructor
ReservationOptionsValuesViewModel = function(data) {
  var self = this;
  debugger
  ko.mapping.fromJS(data, null, self);
}

var reservationOptionsMapping = {
    'ReservationOptions': {
      key: function(reservationOption) {
        return ko.utils.unwrapObservable(reservationOption.Id);
      },
      create: function(options) {
        return new ReservationOptionsViewModel(options.data,
          reservationOptionValuesMapping);
      }
    }
  }
var reservationOptionValuesMapping = {
  'ReservationOptionValues': {
    key: function(reservationOptionValues) {
      return
      ko.utils.unwrapObservable(reservationOptionValues.Id);
    },
    create: function(options) {
      return new ReservationOptionsValuesViewModel(options.data);
    }
  }
}

var formControlsModel = new ReservationViewModel(data);

ko.applyBindings(formControlsModel);