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

1

11

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);
Duelist answered 30/10, 2017 at 14:57 Comment(0)
Y
2

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/

Yen answered 30/10, 2017 at 15:50 Comment(2)
You shouldn't need to add a blank option when using valueAllowUnset. Just set the model value to undefined instead of ''.Stoned
Obviously my code is more complicated than the provided example, and so I have to add a lot of logic before viewModel.selectedStreet(''). I was hoping for a better solution.. Anyway thanks for the workaround, it seems to be working OK.Duelist

© 2022 - 2024 — McMap. All rights reserved.