JSFiddle - React, Tailwind, and code Playground

by Ori Hochman

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/lodash.min.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<div class="panel-body">
  <div class="form-group">
    <label for="availableCitiesSelectControl" class="control-label">Select a City</label>
    <select class="form-control" id="availableCitiesSelectControl" data-bind="foreach: availableCities, value: selectedCity, valueAllowUnset: true">
      <optgroup data-bind="attr: {label: regionName}, foreach: cities">
        <option data-bind="text: cityName"></option>
      </optgroup>
    </select>
  </div>

  <div class="form-group">
    <label for="availableStreetSelectControl" class="control-label">Select a Street</label>
    <select class="form-control" id="availableStreetSelectControl" data-bind="foreach: availbleStreets, value: selectedStreet, valueAllowUnset: true">
     <option value="">Select street...</option>
      <optgroup data-bind="attr: {label: cityName}, foreach: streets">
        <option data-bind="text: streetName"></option>
      </optgroup>
    </select>
  </div>

  <div class="form-group">
    <label for="" class="control-label">Selected City</label>
    <input type="text" data-bind="value: selectedCity" disabled />
  </div>

  <div class="form-group">
    <label for="" class="control-label">Selected Street</label>
    <input type="text" data-bind="value: selectedStreet" disabled />
  </div>
</div>

JavaScript

var ViewModel = function() {

   var self = this;

   var regionAndCityArray = [{
     regionName: "Europe",
     cities: [{
       cityName: "London",
       additionalUnimportantInformation: 100
     }, {
       cityName: "Paris",
       additionalUnimportantInformation: 200
     }]
   }, {
     regionName: "North America",
     cities: [{
       cityName: "New York",
       additionalUnimportantInformation: 45
     }]
   }];

   var cityAndStreetArray = [{
     cityName: "London",
     streets: [{
       streetName: "Parker",
       streetLength: 5
     }, {
       streetName: "Macklin",
       streetLength: 10
     }, ]
   }, {
     cityName: "New York",
     streets: [{
       streetName: "5th Avenue",
       streetLength: 3
     }, {
       streetName: "Park Ave",
       streetLength: 12
     }]
   }, {
     cityName: "Paris",
     streets: [{
       streetName: "Rue de Turbigo",
       streetLength: 11
     }, {
       streetName: "Rue aux Ours",
       streetLength: 12
     }]
   }];

   var getAvailableStreets = function() {

     var availableStreets = cityAndStreetArray;

     var selectedCity = self.selectedCity();

     var selectedRegion = _.find(regionAndCityArray,
       function(region) {
         return _.find(region.cities,
           function(city) {
             return city.cityName === selectedCity;
           });
       });

     if (selectedRegion == undefined) {
       return availableStreets;
     }

     var filteredStreets = _.filter(cityAndStreetArray,
       function(city) {
         return city.cityName === selectedCity;
       });

     return filteredStreets;
   }

   self.availableCities = ko.observableArray(regionAndCityArray);
   self.selectedCity = ko.observable();
   self.availbleStreets = ko.computed(getAvailableStreets);
   self.selectedStreet = ko.observable();

 };
 var viewModel = new ViewModel();
 
 viewModel.selectedCity.subscribe(function() { 
    viewModel.selectedStreet(''); 
}, viewModel); 

...