敲除:在选择另一个元素时从多选控件中删除一个元素

knockout: remove one element from multiselect control when other is selected

我正在使用淘汰赛并且我有一个项目列表,比方说:

Tomato,
Potato,
Broccoli, 
Bean

所有这些项目都允许用户从 multiselect 表单控件select。

<select class="form-control" multiple
  data-bind="selectPicker: Types,
  optionsText: 'Name',
  optionsValue: 'VegetableTypeId',
  selectPickerOptions: { optionsArray: AvailableVegetableTypes }">
</select>

除了一种情况 - 当用户 select 的番茄时,土豆应该不 select。 我试图在 selected 项目数组上使用订阅:

this.Types.subscribe(changes => {
        var VegetableTypes = this.AvailableVegetablesTypes();
        var company = VegetableTypes.First(element => element.VegetableTypeId == changes[0].value);
        if (changes[0].status == "added") {
            if (Vegetable.IsTomato) {
                this.Types.remove(element =>
                    VegetableTypes.First(baseElement =>
                        baseElement.VegetableTypesTypeId == element && baseElement.IsPotato));
            } else if (Vegetable.IsPotato) {
                this.Types.remove(element =>
                    VegetableTypes.First(baseElement =>
                        baseElement.VegetableTypesTypeId == element && baseElement.IsTomato));
            }
        }
    }, null, "arrayChange");

问题是我正在使用 ObservableArray.Remove,所以它会在当前 运行 完成之前再次调用我的函数。这应该不是问题,因为删除后第一个变化是 "deletion" 类型,所以整个逻辑不应该被执行。

但在这之后,当我再次 select tomato/potato 时,什么都没有被触发。 最后我实际上同时拥有番茄和土豆 select编辑。 然后,当我 deselect 这两个中的一个并再次 select 时,一切正常,然后整个情况重复。

你有什么想法吗?

我不明白你为什么使用 selectPicker 绑定而不是正常的 optionsselectedOptions 绑定 available in Knockout.

但是,我构建了一个实现所需行为的简单演示。您可以在这里找到它:

http://jsbin.com/fofojaqohi/1/edit?html,js,console,output

请注意,每当您在 Potato 之后 select Tomato 时,Potato 将变得未selected。

您的方向是对的:您需要订阅 selected 项的数组并检查是否有任何无效的 selection。希望对您有所帮助。

供参考,这里是代码:

HTML:

<select class="form-control" multiple="true"
  data-bind="options: availableVegetableTypes, selectedOptions: selected">
</select>

JS:

var availableVegetableTypes = ['Tomato',
'Potato',
'Broccoli', 
'Bean'];

var selected = ko.observableArray();

var unselectMap = {
  'Tomato': 'Potato'
};

selected.subscribe(function(selectedOptions){
  var keysToUnselect = [];
  console.log("Selected", selectedOptions);
  selectedOptions.forEach(function(selectedOption){
    if (unselectMap[selectedOption] != null) {
      // This key exists in the unselect map
      // Let's check if the value is in the array
      if (_.contains(selectedOptions, unselectMap[selectedOption])) {
        // The invalid key exists. Let's mark it for removal.
        keysToUnselect.push(unselectMap[selectedOption]);
      }
    }
  });
  if (keysToUnselect.length > 0) { 
    console.log("Unselect", keysToUnselect);
    var reject = function(v){
      return _.contains(keysToUnselect, v);
    };
    filteredSelectedOptions = _.reject(selectedOptions, reject);
    console.log("Filtered", filteredSelectedOptions);
    selected(filteredSelectedOptions);
  }
});

ko.applyBindings({
  availableVegetableTypes:availableVegetableTypes,
  selected: selected
});