JSFiddle - React, Tailwind, and code Playground

by Ben Foster

HTML

<script src="https://dl.dropbox.com/u/6378864/countries.js"></script>
<script src="http://knockoutjs.com/downloads/knockout-2.2.1.js"></script>
<div>
    <table>
        <thead>
            <tr>
                <th>Region</th>
                <th>Country</th>
                <th>City</th>
                <th></th>
            </tr>
        </thead>
        <tbody data-bind="foreach: lines">
            <tr>
                <td>
                    <select data-bind="options: newData, optionsText: 'Name', optionsCaption: 'Select a region...', attr: { name: 'SubRegionIndex' + '['+$index()+']' }, value: region"></select>
                </td>
                <td data-bind="with: region">
                    <select data-bind="options: Countries, optionsText: 'Name', optionsCaption: 'Select a country...', attr: { name: 'CountryIndex' + '['+$index()+']' }, value: $parent.country"></select>
                </td>
                <td data-bind="with: country">
                    <select data-bind="options: Cities, optionsText: 'Name', optionsCaption: 'Select a city...', attr: { name: 'CityIndex' + '['+$index()+']' }, value: $parent.city"></select>
                </td>
                <td><a href='#' data-bind='click: $parent.removeLine'>Remove</a>

                </td>
            </tr>
        </tbody>
    </table>
    <button data-bind='click: addLine'>Add more...</button>
</div>
<pre data-bind="text: ko.toJSON($root, null, 2)"></pre>

JavaScript

var existingRows = [{
           "Region": 1,
               "Country": 13,
               "City": 19
       }, {
           "Region": 1,
               "Country": 158,
               "City": 3
       }];

       var Location = function (region, country, city) {
           var self = this;
           self.region = ko.observable(region);
           self.country = ko.observable(country);
           self.city = ko.observable(city);

           // Whenever the region changes, reset the country selection
           self.region.subscribe(function () {
               self.country(undefined);
           });

           // Whenever the country changes, reset the city selection
           self.country.subscribe(function () {
               self.city(undefined);
           });
       };

       var LocationViewModel = function (data) {
           var self = this;
           //self.locationData = ko.observableArray([]);
           //$.ajax({
           //    url: '/GetFullLocationData/',
           //    type: 'GET',
           //    async: false,
           //    success: function (dlist) {
           //        self.locationData(dlist);
           //    }
           //});

           //var grabbedData = self.locationData();

           self.lines = ko.observableArray(ko.utils.arrayMap(data, function (row) {
               var rowRegion = ko.utils.arrayFirst(newData, function (region) {
                   return region.ID == row.Region;
               });
               var rowCountry = ko.utils.arrayFirst(rowRegion.Countries, function (country) {
                   return country.ID == row.Country;
               });
               var rowCity = ko.utils.arrayFirst(rowCountry.Cities, function (city) {
                   return city.ID == row.City;
               });
               return new Location(rowRegion, rowCountry, rowCity);
           }));

           // Operations
           self.addLine = function () {
               self.lines.push(new Location())
           };
...