Knockout/Select2 下拉列表包含正确的选项,但现在没有显示任何选定内容
Knockout/Select2 Dropdownlist contains correct options, but now displays nothing as selected
所以我有两个下拉列表,我正在使用 knockout 和 select。当 availablePeople 列表 returns 全部为 false(无法正常工作)时,我可以 select 并保留我选择的人。但是,当可用人员列表正常工作时,我无法看到我选择的下拉菜单 selection。详细说明 availablePeople 列表,如果您 select 一个人,则该人不能再 select 在以后的行中编辑。
HTML
<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...',
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...',
select2: {placeholder: 'Please select a Person...', allowClear: false}"> </select>
</td>
<td>
<span id="changeTypeSpan" data-bind="text: ChangeType"></span>
</td>
</tr>
</tbody>
</table>
</div>
JS
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.AvailableFrom = ko.computed(function() {
var available = ko.utils.arrayFilter(self.AllFromList, function(item) {
return !ko.utils.arrayFirst(self.PossessionChanges() , function (possessionChange) {
var person = self.GetPersonById(possessionChange.SelectedFrom());
if (person) {
return person.ID === item.ID;
} else {
return false;
}
});
});
return available;
});
self.AvailableTo = ko.computed(function() {
var available = ko.utils.arrayFilter(self.AllToList, function(item) {
return !ko.utils.arrayFirst(self.PossessionChanges() , function (possessionChange) {
var person = self.GetPersonById(possessionChange.SelectedTo());
if (person) {
return person.ID === item.ID;
} else {
return false;
}
});
});
return available;
});
self.addPossessionChange = function () {
self.PossessionChanges.push(new PossessionChangeModel(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());
这也是 jsfiddle:https://jsfiddle.net/cpd5w9he/11/
如果将 person.ID 更改为 person,它可以工作,但选项不正确
原题:
当您 select 下拉列表中的项目时, "SelectedTo" 的值会正确更改为该项目的 ID 值。但是,然后重新计算下拉选项,然后从选项列表中删除该项目,导致 "SelectedTo" 的值恢复为未定义。您不能拥有不属于选项列表的 selected 值。
解法:
这是一个片段,可以做你想要的。我不得不将根对象向下传递到 PossessionChangeVM 中,我对此并不十分满意,但它确实有效。这样,每个 possessionChangeVM 使用 FullList 构建自己的可用选项列表,并仅在项目已被使用但不是当前项目的 selected 时进行过滤。
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.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.PossessionChanges.push(new PossessionChangeVM(self.PossessionChanges().length +1, self));
self.GetPersonById = function (id) {
return ko.utils.arrayFirst(self.AllFromList, function (person) {
return person.ID === ko.unwrap(id);
});
}
self.addPossessionChange = function () {
self.PossessionChanges.push(new PossessionChangeVM(self.PossessionChanges().length + 1, self));
}
self.removePossessionChangeChange = function(possessionChange) {
self.PossessionChanges.remove(possessionChange);
}
}
function PossessionChangeVM(possessionChangeId, root) {
var self = this;
self.possessionChangeId = ko.observable(possessionChangeId);
self.SelectedFrom = ko.observable();
self.SelectedTo = ko.observable();
self.AvailableFrom = ko.computed(function() {
return ko.utils.arrayFilter(root.AllFromList, function(item) {
return root.UsedFrom().indexOf(item.ID) < 0 || item.ID === self.SelectedFrom();
});
});
self.AvailableTo = ko.computed(function() {
return ko.utils.arrayFilter(root.AllToList, function(item) {
return root.UsedTo().indexOf(item.ID) < 0 || item.ID === self.SelectedTo();
});
});
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: AvailableFrom,
value: SelectedFrom,
optionsText: function(i) {return i.Name},
optionsValue: function(i) {return i.ID},
optionsCaption: 'Please select a Person...',
select2: { placeholder: 'Please select a Person...', allowClear: false}"> </select>
</td>
<td>
<select class="form-control"
data-bind="options: AvailableTo,
value: SelectedTo,
optionsText: function(i) {return i.Name},
optionsValue: function(i) {return i.ID},
optionsCaption: 'Please select a Person...',
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>
所以我有两个下拉列表,我正在使用 knockout 和 select。当 availablePeople 列表 returns 全部为 false(无法正常工作)时,我可以 select 并保留我选择的人。但是,当可用人员列表正常工作时,我无法看到我选择的下拉菜单 selection。详细说明 availablePeople 列表,如果您 select 一个人,则该人不能再 select 在以后的行中编辑。
HTML
<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...',
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...',
select2: {placeholder: 'Please select a Person...', allowClear: false}"> </select>
</td>
<td>
<span id="changeTypeSpan" data-bind="text: ChangeType"></span>
</td>
</tr>
</tbody>
</table>
</div>
JS
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.AvailableFrom = ko.computed(function() {
var available = ko.utils.arrayFilter(self.AllFromList, function(item) {
return !ko.utils.arrayFirst(self.PossessionChanges() , function (possessionChange) {
var person = self.GetPersonById(possessionChange.SelectedFrom());
if (person) {
return person.ID === item.ID;
} else {
return false;
}
});
});
return available;
});
self.AvailableTo = ko.computed(function() {
var available = ko.utils.arrayFilter(self.AllToList, function(item) {
return !ko.utils.arrayFirst(self.PossessionChanges() , function (possessionChange) {
var person = self.GetPersonById(possessionChange.SelectedTo());
if (person) {
return person.ID === item.ID;
} else {
return false;
}
});
});
return available;
});
self.addPossessionChange = function () {
self.PossessionChanges.push(new PossessionChangeModel(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());
这也是 jsfiddle:https://jsfiddle.net/cpd5w9he/11/ 如果将 person.ID 更改为 person,它可以工作,但选项不正确
原题: 当您 select 下拉列表中的项目时, "SelectedTo" 的值会正确更改为该项目的 ID 值。但是,然后重新计算下拉选项,然后从选项列表中删除该项目,导致 "SelectedTo" 的值恢复为未定义。您不能拥有不属于选项列表的 selected 值。
解法: 这是一个片段,可以做你想要的。我不得不将根对象向下传递到 PossessionChangeVM 中,我对此并不十分满意,但它确实有效。这样,每个 possessionChangeVM 使用 FullList 构建自己的可用选项列表,并仅在项目已被使用但不是当前项目的 selected 时进行过滤。
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.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.PossessionChanges.push(new PossessionChangeVM(self.PossessionChanges().length +1, self));
self.GetPersonById = function (id) {
return ko.utils.arrayFirst(self.AllFromList, function (person) {
return person.ID === ko.unwrap(id);
});
}
self.addPossessionChange = function () {
self.PossessionChanges.push(new PossessionChangeVM(self.PossessionChanges().length + 1, self));
}
self.removePossessionChangeChange = function(possessionChange) {
self.PossessionChanges.remove(possessionChange);
}
}
function PossessionChangeVM(possessionChangeId, root) {
var self = this;
self.possessionChangeId = ko.observable(possessionChangeId);
self.SelectedFrom = ko.observable();
self.SelectedTo = ko.observable();
self.AvailableFrom = ko.computed(function() {
return ko.utils.arrayFilter(root.AllFromList, function(item) {
return root.UsedFrom().indexOf(item.ID) < 0 || item.ID === self.SelectedFrom();
});
});
self.AvailableTo = ko.computed(function() {
return ko.utils.arrayFilter(root.AllToList, function(item) {
return root.UsedTo().indexOf(item.ID) < 0 || item.ID === self.SelectedTo();
});
});
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: AvailableFrom,
value: SelectedFrom,
optionsText: function(i) {return i.Name},
optionsValue: function(i) {return i.ID},
optionsCaption: 'Please select a Person...',
select2: { placeholder: 'Please select a Person...', allowClear: false}"> </select>
</td>
<td>
<select class="form-control"
data-bind="options: AvailableTo,
value: SelectedTo,
optionsText: function(i) {return i.Name},
optionsValue: function(i) {return i.ID},
optionsCaption: 'Please select a Person...',
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>