Мне удалось собрать другой фрагмент, используя вашу идею из предыдущего вопроса, чтобы использовать атрибут disabled вместо полного удаления элементов. Knockout имеет удобную привязку опций выбора, называемую optionsAfterRender, которая позволяет выполнить некоторую постобработку сгенерированных опций.
ko.bindingHandlers.select2 = {
init: function(element, valueAccessor, allBindingsAccessor, bindingContext) {
ko.utils.domNodeDisposal.addDisposeCallback(element,
function() {
$(element).select2('destroy');
});
var select2 = ko.utils.unwrapObservable(allBindingsAccessor().select2);
$(element).select2(select2);
},
update: function(element, valueAccessor, allBindingsAccessor, bindingContext) {
var allBindings = allBindingsAccessor();
if ("value" in allBindings) {
if ((allBindings.select2.multiple || element.multiple) && allBindings.value().constructor != Array) {
$(element).val(allBindings.value().split(',')).trigger('change');
} else {
$(element).val(allBindings.value()).trigger('change');
}
}
$(element).trigger("change");
}
};
function BookPossessionTransferVM() {
var self = this;
self.AllFromList = ([{ "IsAdult": false, "Name": "Bob", "ID": 38438 }, { "IsAdult": false, "Name": "Gordon", "ID": 54686 }, { "IsAdult": true, "Name": "Bill", "ID": 45645 }, { "IsAdult": false, "Name": "Sue", "ID": 1231 }, { "IsAdult": false, "Name": "Ling", "ID": 123578 }, { "IsAdult": false, "Name": "Ivy", "ID": 78945 }]);
self.AllToList = ([{ "IsAdult": false, "Name": "Adam", "ID": 38438 }, { "IsAdult": false, "Name": "Geoff", "ID": 54686 }, { "IsAdult": true, "Name": "Josh", "ID": 45645 }, { "IsAdult": false, "Name": "Sam", "ID": 1231 }, { "IsAdult": false, "Name": "Ming", "ID": 123578 }, { "IsAdult": false, "Name": "Austin", "ID": 78945 }, { "IsAdult": false, "Name": "Tsz", "ID": 78945 }, { "IsAdult": true, "Name": "Ireylnn", "ID": 78945 }, { "IsAdult": true, "Name": "Isabelle", "ID": 78945 }, { "IsAdult": true, "Name": "Vickey", "ID": 78945 }]);
self.PossessionChanges = ko.observableArray([]);
self.PossessionChanges.push(new PossessionChangeVM(self.PossessionChanges().length + 1));
self.GetPersonById = function(id) {
return ko.utils.arrayFirst(self.AllFromList, function(person) {
return person.ID === ko.unwrap(id);
});
}
self.UsedTo = ko.computed(function() {
return self.PossessionChanges()
.filter(function(item) {
return item.SelectedTo() != undefined;
})
.map(function(item) {
return item.SelectedTo();
});
});
self.UsedFrom = ko.computed(function() {
return self.PossessionChanges()
.filter(function(item) {
return item.SelectedFrom() != undefined;
})
.map(function(item) {
return item.SelectedFrom();
});
});
self.setOptionDisable = function(option, item) {
if (item)
ko.applyBindingsToNode(option, {
disable: item.disabled
}, item);
}
self.AvailableFrom = ko.computed(function() {
var available = self.AllFromList.slice();
for (var i = 0; i < available.length; i++) {
available[i].disabled = available[i].disabled || ko.observable(false);
available[i].disabled((self.UsedFrom().indexOf(available[i].ID) >= 0));
}
return available;
});
self.AvailableTo = ko.computed(function() {
var available = self.AllToList.slice();
for (var i = 0; i < available.length; i++) {
available[i].disabled = available[i].disabled || ko.observable(false);
available[i].disabled((self.UsedTo().indexOf(available[i].ID) >= 0));
}
return available;
});
self.addPossessionChange = function() {
self.PossessionChanges.push(new PossessionChangeVM(self.PossessionChanges().length + 1));
}
self.removePossessionChangeChange = function(possessionChange) {
self.PossessionChanges.remove(possessionChange);
}
}
function PossessionChangeVM(possessionChangeId) {
var self = this;
self.possessionChangeId = ko.observable(possessionChangeId);
self.SelectedFrom = ko.observable();
self.SelectedTo = ko.observable();
self.ChangeType = ko.pureComputed(function() {
if (self.SelectedFrom() !== undefined && self.SelectedTo() !== undefined) {
return 'Update';
} else if (self.SelectedFrom() === undefined && self.SelectedTo() === undefined) {
return '';
} else if (self.SelectedFrom() === undefined) {
return 'Add';
} else if (self.SelectedTo() === undefined) {
return 'Remove';
} else {
return '';
}
});
}
function SelectedPerson(isAdult, name, id) {
var self = this;
self.IsAdult = ko.observable(isAdult);
self.Name = ko.observable(name);
self.ID = ko.observable(id);
}
ko.applyBindings(new BookPossessionTransferVM());
#tblPossessionChanges {
width: 70%;
height: 100px;
text-align: center;
table-layout: fixed;
}
#tblPossessionChanges td,
#tblPossessionChanges th {
padding: 1rem;
}
#tblPossessionChanges thead th {
text-align: center;
}
#tblPossessionChanges thead th:first-child {
text-align: left;
width: 10%;
}
#tblPossessionChanges tbody td:first-child {
text-align: left;
width: 10%;
}
#tblPossessionChanges>tbody>tr>td.prompt>a {
font-weight: bold;
}
#tblPossessionChanges tbody td select {
width: 75%
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.12/js/select2.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.6-rc.0/css/select2.min.css" rel="stylesheet" />
<div>
<table id="tblPossessionChanges">
<thead>
<tr>
<th><a href="#" class="buttonSmall" data-bind="click: addPossessionChange">Add</a></th>
<th>From</th>
<th>To</th>
</tr>
</thead>
<tbody data-bind="foreach: PossessionChanges">
<tr>
<td class="prompt">
<a href="#" class="buttonSmall" data-bind="click: $root.removePossessionChange">Delete</a> </td>
<td>
<select class="form-control"
data-bind="options: $root.AvailableFrom,
value: SelectedFrom,
optionsText: function(i) {return i.Name},
optionsValue: function(i) {return i.ID},
optionsCaption: 'Please select a Person...',
optionsAfterRender: $root.setOptionDisable,
select2: { placeholder: 'Please select a Person...', allowClear: false}">
</select>
</td>
<td>
<select class="form-control"
data-bind="options: $root.AvailableTo,
value: SelectedTo,
optionsText: function(i) {return i.Name},
optionsValue: function(i) {return i.ID},
optionsCaption: 'Please select a Person...',
optionsAfterRender: $root.setOptionDisable,
select2: { placeholder: 'Please select a Person...', allowClear: false}">
</select>
</td>
<td>
<span id="changeTypeSpan" data-bind="text: ChangeType"></span>
</td>
</tr>
</tbody>
</table>
<br/>
</div>