3

I'm trying to manually render options inside of optgroups in knockout.js with guidance from Ryan Niemeyer's answer here. It works fine, but only if the options are rendered immediately. If they're loaded subsequently, then the correct item does not select.

The code and fiddle link are below. Changing

t: ko.observable(null)

to

t: ko.observable(t)

makes it work, so the problem is definitely the delayed loading, and I have valueAllowUnset set to true.

fiddle: http://jsfiddle.net/exyy7hq2/3/

HTML

<select data-bind="template: { name: 'selectTemplateType', data: t }, valueAllowUnset: true, value: selectedId"></select>
<div data-bind='text: selectedId'></div>

<script type="text/html" id="selectTemplateType">
    <!-- ko ifnot: $data -->
        <option>No template selected</option>
    <!-- /ko -->
    <!-- ko if: $data -->
        <option>Select</option>
        <!-- ko foreach: groups -->
            <optgroup data-bind="attr: { 'label': name }, foreach: types">
                <option data-bind="option: id, text: name"></option>
            </optgroup>
        <!-- /ko -->
    <!-- /ko -->
</script>

JavaScript

ko.bindingHandlers.option = {
    update: function(element, valueAccessor) {
       var value = ko.utils.unwrapObservable(valueAccessor());
       ko.selectExtensions.writeValue(element, value);   
    }        
};

var t = {
    groups: [
        { name: 'a', types: [{id: 1, name: 't1'}] },
        { name: 'b', types: [{id: 102, name: 't2'}, {id: 103, name: 't3'}] },
        { name: 'c', types: [{id: 5, name: 'x'}] }
    ]
}
var vm = {
    t: ko.observable(null),
    selectedId: ko.observable(103)
};
ko.applyBindings(vm);
setTimeout(function(){ vm.t(t); }, 2000);
Community
  • 1
  • 1
Adam Rackis
  • 82,527
  • 56
  • 270
  • 393

1 Answers1

0

It seems if you're not using the options binding, Knockout simply has no way of adjusting things when you dynamically generate new options, even with valueAllowUnset set to true, so my approach simply won't work; the correct options need to be there when bound. One decent way to achieve this is to render the select only after the values are present, like below (and also the option custom binding is not needed - just a regular value binding on the option element).

http://jsfiddle.net/exyy7hq2/11/

<!-- ko if: t -->
<select data-bind="template: { name: 'selectTemplateType', data: t }, value: selectedId"></select>
<!-- /ko -->
<!-- ko ifnot: t -->
<select>
    <option>No templates loaded</option>
</select>
<!-- /ko -->
<div data-bind='text: selectedId'></div>

<script type="text/html" id="selectTemplateType">
    <option>Select</option>
    <!-- ko foreach: groups -->
        <optgroup data-bind="attr: { 'label': name }, foreach: types">
            <option data-bind="value: id, text: name"></option>
        </optgroup>
    <!-- /ko -->
</script>
Adam Rackis
  • 82,527
  • 56
  • 270
  • 393