Knockout Select2 按对象设置初始值

Knockout Select2 set initial value by object

我有一个 Knockout.js 网络应用程序,其中有一个 select2 下拉菜单。我想将 id 和文本值都绑定到一个变量而不仅仅是 id。这是我的数据:

var cars = [{id: 1, name: 'Honda'}, {id: 2, name: 'Toyota'}, {id: 3, name: 'Dodge'}];
var selectedCar = ko.observable();

这是我的 html:

<select data-bind="value: selectedCar, optionsCaption: 'Select', optionsText: 'name', options: cars"></select>

所以现在,每当我 select 下拉列表中的内容时,我的变量包含整个对象,如下所示:

selectedCar = {id: 1, name: 'Honda'};

唯一的问题出现在您加载页面并希望将下拉列表设置为特定值时。即使在呈现 html 之前,selectedCar 变量设置为 {id: 1, name: 'Honda'},当页面呈现下拉列表时未设置任何内容,它只是设置为占位符 'Select'。

我做错了什么?

如果要将 id 和文本值都绑定到一个变量,则需要使用 optionsValue 绑定并将整个上下文绑定到它 ($data);

<select data-bind="value: selectedCar, 
                   optionsCaption: 'Select', 
                   optionsText: 'name', 
                   options: cars, 
                   optionsValue: $data"></select>

通常我们会指定 id 或其他任何内容来选择初始值。因此,在您的问题中,如果湿设置 optionsValue: $data 而不是 $optionsValue: 'id',则将整个对象 {id: 1, name: 'Honda'} 作为初始值提供给 selectedCar 是有意义的。

(IMPORTANT) 但事实证明这是行不通的,因为我们正在创建一个新对象,因此当 Knockout 比较 [= 的对象时,它的相等性测试将失败20=] 里面的对象 selectedCar。设置初始值的正确方法是cars[0].


我确定这是一个打字错误,但我还是要说明:当你创建任何需要被 HTML 访问的变量时,你需要将它绑定到 this,这将是对 viewModel 的引用。

this.cars = [{id: 1, name: 'Honda'}, {id: 2, name: 'Toyota'}, {id: 3, name: 'Dodge'}];
this.selectedCar = ko.observable();

让我们用 fiddle 来测试这一切:

var viewModel = function(){
  var self = this;
  self.cars = [{id: 1, name: 'Honda'}, {id: 2, name: 'Toyota'}, {id: 3, name: 'Dodge'}];
  self.selectedCar = ko.observable(self.cars[0]);
  

};

ko.applyBindings(new viewModel());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<select data-bind="value: selectedCar, optionsCaption: 'Select', optionsText: 'name', optionsValue:$data, options: cars"></select>

<!-- to verify that we are getting the entire object -->
<p data-bind="text: ko.toJSON(selectedCar)"></p>

select框的value将成为对应select编辑的项目或optionsValue参数中设置的属性的对象.因此,对于对象,您设置的 selected 值必须与数组中存在的实例相同。拥有一个对象的不同实例恰好在结构上是等价的是不够的。

对于这种情况,我发现将 select 框的值绑定到对象的唯一 ID 更容易。然后你可以通过计算值将该 id 映射到你想要的实际实例。

function ViewModel(data) {
    this.cars = data.cars;
    this.selectedCarId = ko.observable(data.selectedCarId);
    this.selectedCar = ko.computed(() => {
        let selectedCarId = this.selectedCarId();
        return this.cars.find(c => c.id === selectedCarId);
    });
}

let model = {
    cars: [
        { id: 1, name: 'Honda' },
        { id: 2, name: 'Toyota' },
        { id: 3, name: 'Dodge' }
    ],
    selectedCarId: 2
};
ko.applyBindings(new ViewModel(model), document.getElementById('content'));
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.5/js/select2.min.js"></script>
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.5/css/select2.min.css">
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>

<div id="content">
  <select data-bind="value: selectedCarId,
                     optionsCaption: 'Select',
                     optionsText: 'name',
                     optionsValue: 'id',
                     options: cars">
  </select>
  <p>selectedCarId: <span data-bind="text: selectedCarId"></span></p>
  <p>selectedCar: <span data-bind="text: ko.toJSON(selectedCar)"></span></p>
  
  <pre data-bind="text: ko.toJSON($root, null, 2)"></pre>
</div>

如果您不想使用单独的计算 属性,您仍然需要传入一个索引,但您设置的值必须从数组中获取。

function ViewModel(data) {
    this.cars = data.cars;
    this.selectedCar = ko.observable(
        data.cars.find(c => c.id === data.selectedCarId)
    );
}