Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Knockoutjs - lost two way binding to select value when using foreach instead of options

I have two select controls.

One is dependent on the other. For a simple example, let's assume the first displays a list of cities, while the other displays a list of streets in each city.

When the page initially loads, the select control displaying the streets is showing all the available streets. However, once the user chooses a city in the first select, the second select is filtered to display streets belonging to the selected city only.

This works OK when using the options binding, however, I need the ability to generate optgroups and options binding does not support it, so I have to use the foreach binding.

The result is that whenever a city is selected, two unintended consequences occur:

  1. The second select (the filtered list of streets) appear to have the first street of the selected city chosen, even though I'm using valueAllowUnset: true. This is not reflected in the view model
  2. When actually choosing a street in the second select and then choosing a different city in the first select, the second select updates properly to reflect the changes in the list, but the view model does not, thereby still retaining the previously selected value (even though it's not in the list anymore). Even If I remove valueAllowUnset: true from the second select, the issue still remains.

Is there any workaround to this issue? I really have to use the foreach binding instead of the options binding.

JSFiddle: https://jsfiddle.net/jfxovLna/13/

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();
ko.applyBindings(viewModel);
like image 245
Avidan Chen Avatar asked Oct 30 '17 14:10

Avidan Chen


1 Answers

First, add an empty option to your select input.

<option value="">Select Street</option>

Now subscribe to the selectedCity property of your view model. Whenever it changes, programmatically set the selectedStreet to ''.

viewModel.selectedCity.subscribe(function() { 
  viewModel.selectedStreet(''); 
}, viewModel); 

This way you can solve both your issues.

Made the changes in your fiddle and it works. tries to update it.

Here is a fiddle - https://jsfiddle.net/Shabi_669/w1vcjbjo/

like image 95
Cookie Monster Avatar answered Nov 10 '22 07:11

Cookie Monster