KnockoutJSを使用してoptgroupsで選択リストを作成する例を見つけました ここ 。これは正常に機能しますが、ドロップダウンの値を自分のjavascriptオブジェクトにバインドしてから、そのオブジェクトの特定のプロパティにアクセスします。
<select data-bind="foreach: groups, value:selectedOption">
<optgroup data-bind="attr: {label: label}, foreach: children">
<option data-bind="text: label"></option>
</optgroup>
</select>
function Group(label, children) {
this.label = ko.observable(label);
this.children = ko.observableArray(children);
}
function Option(label, property) {
this.label = ko.observable(label);
this.someOtherProperty = ko.observable(property);
}
var viewModel = {
groups: ko.observableArray([
new Group("Group 1", [
new Option("Option 1", "A"),
new Option("Option 2", "B"),
new Option("Option 3", "C")
]),
new Group("Group 2", [
new Option("Option 4", "D"),
new Option("Option 5", "E"),
new Option("Option 6", "F")
])
]),
selectedOption: ko.observable(),
specialProperty: ko.computed(function(){
this.selectedOption().someOtherProperty();
})
};
ko.applyBindings(viewModel);
この状況に適した選択は、「手作り」オプションがoptions
バインディングによって作成されたオプションと同じように動作するようにするクイックカスタムバインディングを作成することです(オブジェクトをメタデータとしてアタッチします)。バインディングは単純に次のようになります。
ko.bindingHandlers.option = {
update: function(element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor());
ko.selectExtensions.writeValue(element, value);
}
};
次のように使用します。
<select data-bind="foreach: groups, value: selectedOption">
<optgroup data-bind="attr: {label: label}, foreach: children">
<option data-bind="text: label, option: $data"></option>
</optgroup>
</select>
ここのサンプル: http://jsfiddle.net/rniemeyer/aCS7D/
キャプション付きのこのバージョンと、親アイテムを選択する場合:
<select data-bind="value: selectedOption ">
<option data-bind="value:'', text:'Select'"></option>
<!-- ko foreach: groups -->
<optgroup data-bind="attr:{label: label}">
<option data-bind="value: $data, text:label"></option>
<!-- ko foreach: children -->
<option data-bind="value: $data, text:label"></option>
<!-- /ko -->
</optgroup>
<!-- /ko -->
</select>
キャプション、グループ、オプションを完全に制御し、select要素の値バインディングでこれを機能させる場合は、selectの値バインディングがバインドされる前に、すべてのオプションがバインドされていることを確認する必要があります。それ以外の場合、キャプションオプションが追加されると、値バインディングはselectedOptionobservableを空にします。
たとえば、カスタムvalueAfterChildren
バインディングを追加できます。これは、値バインディングをラップし、 子が最初にバインドされる であることを確認します。バインディングを適用する前に、少し簡略化されたビューモデルで「オプション5」を事前に選択している以下の例を見てください。
ko.bindingHandlers['valueAfterChildren'] = {
init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
ko.applyBindingsToDescendants(bindingContext, element);
ko.bindingHandlers['value'].init(element, valueAccessor, allBindings, viewModel, bindingContext);
return { controlsDescendantBindings: true };
},
value: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
ko.bindingHandlers['value'].update(element, valueAccessor, allBindings, viewModel, bindingContext);
}
}
var viewModel = {
groups: [
{
label: "Group 1",
children: [{ label: "Option 1" }, { label: "Option 2" }, { label: "Option 3" }]
}, {
label: "Group 2",
children: [{ label: "Option 4" }, { label: "Option 5" }, { label: "Option 6" }]
}
],
selectedOption: ko.observable()
};
viewModel.selectedOption.subscribe(function(newVal) {
console.log(`selected ${!!newVal ? newVal.label : 'empty option'}`);
});
viewModel.selectedOption(viewModel.groups[1].children[1]);
ko.applyBindings(viewModel);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<select data-bind="valueAfterChildren: selectedOption">
<option value="">Select</option>
<!-- ko foreach: groups -->
<optgroup data-bind="attr: {label: label}, foreach: children">
<option data-bind="value: $data, text: label"></option>
</optgroup>
<!-- /ko -->
</select>