Knockoutjs select with option group
Asked Answered
U

4

19

Is there any way in Knockoutjs binding where I can specify optionsGroup ? something like follwoing

<select data-bind="options: collection, optionsText: 'Text', optionsGroup: 'Group'/>

Please do reply.

Unitarian answered 23/1, 2012 at 13:27 Comment(2)
I guess you could use the attr binding as documented in knockoutjs.com/documentation/attr-binding.htmlSidney
Here is a thread about adding optionsGroup support: github.com/SteveSanderson/knockout/pull/94Lindholm
U
31

I got the answer of the same, here is the answer if anybody wants,

<td><select class="fieldValue" data-bind="foreach: $root.AvailableFields, value: FieldId, event :{ change: $root.onFieldSelectionChange}">
        <optgroup data-bind="attr: {label: FieldGroupName}, foreach: Fields">
            <option data-bind="text: HeaderText, value: FieldId"></option>
        </optgroup>
     </select>
  </td>
Unitarian answered 15/2, 2012 at 11:55 Comment(1)
could you please show us the javascript part?? I'm really interested!! ThanksRaynata
G
8

In many cases, you don't need the options themselves to be observable, just the result of the selection. Here is the example which does UK county selection.

HTML:

<div class="form-group">
    <label for="county">County</label>
        <select class="fieldValue" data-bind="foreach: $root.countyList, value: orgCounty">
            <optgroup data-bind="attr: {label: label}, foreach: counties">
            <option data-bind="text: label, value: label"></option>
            </optgroup>
        </select>
</div>

JS :

viewModel.countyList = getCountyList();
viewModel.orgCounty = ko.observable('London'); // Put default value here

function getCountyList() {
    var $arrayCounties = [
        { "label" : "England", "counties" : [ 
            { "label" : "Bedfordshire"},
            { "label" : "Berkshire"},
            { "label" : "Bristol"},
            { "label" : "Buckinghamshire"},
            { "label" : "Cambridgeshire"},
            { "label" : "Cheshire"},
            { "label" : "City of London"},
            { "label" : "Cornwall"},
            { "label" : "Cumbria"},
            { "label" : "Derbyshire"},
            { "label" : "Devon"},
            { "label" : "Dorset"},
            { "label" : "Durham"},
            { "label" : "East Riding of Yorkshire"},
            { "label" : "East Sussex"},
            { "label" : "Essex"},
            { "label" : "Gloucestershire"},
            { "label" : "Greater London"},
            { "label" : "Greater Manchester"},
            { "label" : "Hampshire"},
            { "label" : "Herefordshire"},
            { "label" : "Hertfordshire"},
            { "label" : "Isle of Wight"},
            { "label" : "Kent"},
            { "label" : "Lancashire"},
            { "label" : "Leicestershire"},
            { "label" : "Lincolnshire"},
            { "label" : "Merseyside"},
            { "label" : "Norfolk"},
            { "label" : "North Yorkshire"},
            { "label" : "Northamptonshire"},
            { "label" : "Northumberland"},
            { "label" : "Nottinghamshire"},
            { "label" : "Oxfordshire"},
            { "label" : "Rutland"},
            { "label" : "Shropshire"},
            { "label" : "Somerset"},
            { "label" : "South Yorkshire"},
            { "label" : "Staffordshire"},
            { "label" : "Suffolk"},
            { "label" : "Surrey"},
            { "label" : "Tyne and Wear"},
            { "label" : "Warwickshire"},
            { "label" : "West Midlands"},
            { "label" : "West Sussex"},
            { "label" : "West Yorkshire"},
            { "label" : "Wiltshire"},
            { "label" : "Worcestershire"} ]},
        { "label" : "Wales", "counties" : [ 
            { "label" : "Anglesey"},
            { "label" : "Brecknockshire"},
            { "label" : "Caernarfonshire"},
            { "label" : "Carmarthenshire"},
            { "label" : "Cardiganshire"},
            { "label" : "Denbighshire"},
            { "label" : "Flintshire"},
            { "label" : "Glamorgan"},
            { "label" : "Merioneth"},
            { "label" : "Monmouthshire"},
            { "label" : "Montgomeryshire"},
            { "label" : "Pembrokeshire"},
            { "label" : "Radnorshire"} ]},
        { "label" : "Scotland", "counties" : [ 
            { "label" : "Aberdeenshire"},
            { "label" : "Angus"},
            { "label" : "Argyllshire"},
            { "label" : "Ayrshire"},
            { "label" : "Banffshire"},
            { "label" : "Berwickshire"},
            { "label" : "Buteshire"},
            { "label" : "Cromartyshire"},
            { "label" : "Caithness"},
            { "label" : "Clackmannanshire"},
            { "label" : "Dumfriesshire"},
            { "label" : "Dunbartonshire"},
            { "label" : "East Lothian"},
            { "label" : "Fife"},
            { "label" : "Inverness-shire"},
            { "label" : "Kincardineshire"},
            { "label" : "Kinross"},
            { "label" : "Kirkcudbrightshire"},
            { "label" : "Lanarkshire"},
            { "label" : "Midlothian"},
            { "label" : "Morayshire"},
            { "label" : "Nairnshire"},
            { "label" : "Orkney"},
            { "label" : "Peeblesshire"},
            { "label" : "Perthshire"},
            { "label" : "Renfrewshire"},
            { "label" : "Ross-shire"},
            { "label" : "Roxburghshire"},
            { "label" : "Selkirkshire"},
            { "label" : "Shetland"},
            { "label" : "Stirlingshire"},
            { "label" : "Sutherland"},
            { "label" : "West Lothian"},
            { "label" : "Wigtownshire"} ]},
        { "label" : "Northern Ireland", "counties" : [ 
            { "label" : "Antrim"},
            { "label" : "Armagh"},
            { "label" : "Down"},
            { "label" : "Fermanagh"},
            { "label" : "Londonderry"},
            { "label" : "Tyrone"} ]}
        ];

    return $arrayCounties;
}

You will retrieve the selected option in viewModel.countyList.

Ghiselin answered 15/6, 2015 at 6:55 Comment(0)
C
1

This will also allow a placeholder:

<select class="form-control needsclick" data-bind="value: Value">
  <option value="" disabled selected>Select Value...</option>
  <!-- ko foreach: Group -->
    <optgroup data-bind="attr: { label: Label }, foreach: Collection">
      <option data-bind="text: Label, value: $data"></option>
    </optgroup>
  <!-- /ko -->
</select>

JS would be in this format:

self.Group = ko.pauseableComputed(function () {
   var groups = [
     { 
       Label: 'Group 1',
       Collection: [
          { Label: 'Data 1', Value: 'Data 1' },
          { Label: 'Data 2', Value: 'Data 2' },
       ]
     },
     { 
       Label: 'Group 2',
       Collection: [
          { Label: 'Data 3', Value: 'Data 3' },
          { Label: 'Data 4', Value: 'Data 4' },
       ]
     },
   ]
   return groups;
})

This option value, could be value in this simple instance.

<option data-bind="text: Label, value: Value"></option>

But for more complex object $data would be the better option.

Choosy answered 30/3, 2017 at 10:38 Comment(0)
P
0

Try this.

(function () {
ko.bindingHandlers["groupedOptions"] = {
    update: function (element, valueAccessor, allBindingsAccessor) {
        if (element.tagName != "SELECT")
            throw new Error("groupedOptions binding applies only to SELECT elements");

        var previousSelectedValues = [];
        for (var i = 0; i < element.childNodes.length; i++) {
            var node = element.childNodes[i];
            if (node.tagName == "OPTGROUP") {
                if (node.childNodes != undefined) {
                    for (var k = 0; k < node.childNodes.length; k++) {
                        var childNode = node.childNodes[k];
                        if (childNode.tagName && childNode.tagName && childNode.tagName == "OPTION" && childNode.selected) {
                            previousSelectedValues.push(ko.selectExtensions.readValue(childNode));
                        }
                    }
                }
            } else if (node.tagName && node.tagName == "OPTION" && node.selected) {
                previousSelectedValues.push(ko.selectExtensions.readValue(node));
            }
        }

        var previousScrollTop = element.scrollTop;

        var value = ko.utils.unwrapObservable(valueAccessor());

        // Clear existing elements
        element.innerHTML = "";

        if (value) {
            var allBindings = allBindingsAccessor();
            if (typeof value.length != "number")
                value = [value];
            if (allBindings['optionsCaption']) {
                var option = document.createElement("OPTION");
                option.innerHTML = allBindings['optionsCaption'];
                ko.selectExtensions.writeValue(option, undefined);
                element.appendChild(option);
            }

            var optionsGroupNamesValue = allBindings['optionsGroupNames'];

            // Group values into optgroups
            var groupedOptions = [];
            var optionsGroupValue = allBindings['optionsGroup']; // undefined if not given
            for (var i = 0, j = value.length; i < j; i++) {
                var optionsGroup = null;
                if (typeof optionsGroupValue == "function")
                    optionsGroup = optionsGroupValue(value[i]);
                else if (typeof optionsGroupValue == "string")
                    optionsGroup = value[i][optionsGroupValue];
                else
                    optionsGroup = "";
                if (typeof groupedOptions[optionsGroup] == "undefined")
                    groupedOptions[optionsGroup] = [];

                groupedOptions[optionsGroup].push(value[i]);
            }

            // Create HTML elements
            for (var groupName in groupedOptions) {
                var optgroup = null;
                // Add an OPTGROUP for all groups except for ""
                if (groupName != "") {
                    optgroup = document.createElement("OPTGROUP");
                    optgroup.label = groupName;
                    element.appendChild(optgroup);
                }

                // Create HTML elements for options within this group
                for (var i = 0, j = groupedOptions[groupName].length; i < j; i++) {
                    var valueGroup = groupedOptions[groupName];
                    var option = document.createElement("OPTION");
                    var optionValue = typeof allBindings['optionsValue'] == "string" ? valueGroup[i][allBindings['optionsValue']] : valueGroup[groupName][i];

                    // Pick some text to appear in the drop-down list for this data value
                    var optionsTextValue = allBindings['optionsText'];
                    if (typeof optionsTextValue == "function")
                        optionText = optionsTextValue(valueGroup[i]); // Given a function; run it against the data value
                    else if (typeof optionsTextValue == "string")
                        optionText = valueGroup[i][optionsTextValue]; // Given a string; treat it as a property name on the data value
                    else
                        optionText = optionValue; // Given no optionsText arg; use the data value itself

                    optionValue = ko.utils.unwrapObservable(optionValue);
                    optionText = ko.utils.unwrapObservable(optionText);
                    ko.selectExtensions.writeValue(option, optionValue);

                    option.innerHTML = optionText.toString();

                    if (optgroup != null)
                        optgroup.appendChild(option);
                    else
                        element.appendChild(option);
                }
            }

            // IE6 doesn't like us to assign selection to OPTION nodes before they're added to the document.
            // That's why we first added them without selection. Now it's time to set the selection.
            var newOptions = element.getElementsByTagName("OPTION");
            var countSelectionsRetained = 0;

            for (var i = 0, j = newOptions.length; i < j; i++) {
                if (ko.utils.arrayIndexOf(previousSelectedValues, ko.selectExtensions.readValue(newOptions[i])) >= 0) {
                    ko.utils.setOptionNodeSelectionState(newOptions[i], true);
                    countSelectionsRetained++;
                }
            }

            if (previousScrollTop)
                element.scrollTop = previousScrollTop;
        }
    }
};

ko.bindingHandlers['selectedOptions'] = {
    getSelectedValuesFromSelectNode: function (selectNode) {
        var result = [];
        var nodes = selectNode.childNodes;
        for (var i = 0, j = nodes.length; i < j; i++) {
            var node = nodes[i];
            if ((node.tagName == "OPTGROUP") && node.childNodes != null) {
                var subResult = this.getSelectedValuesFromSelectNode(node);
                for (var k = 0; k < subResult.length; k++) {
                    result.push(subResult[k]);
                }
            }
            else {
                if ((node.tagName == "OPTION") && node.selected)
                    result.push(ko.selectExtensions.readValue(node));
            }
        }

        return result;
    },
    setSelectedValuesFromSelectNode: function (selectNode, newValue) {
        var nodes = selectNode.childNodes;
        for (var i = 0; i < nodes.length; i++) {
            var node = nodes[i];
            if (node.tagName == "OPTION") {
                ko.utils.setOptionNodeSelectionState(node, ko.utils.arrayIndexOf(newValue, ko.selectExtensions.readValue(node)) >= 0);
            }
            else if (node.tagName == "OPTGROUP") {
                for (var k = 0; k < node.childNodes.length; k++) {
                    var childNode = node.childNodes[k];
                    if (childNode.tagName && childNode.tagName == "OPTION") {
                        ko.utils.setOptionNodeSelectionState(childNode, ko.utils.arrayIndexOf(newValue, ko.selectExtensions.readValue(childNode)) >= 0);
                    }
                }
            }
        }
    },
    'init': function (element, valueAccessor, allBindingsAccessor) {
        ko.utils.registerEventHandler(element, "change", function () {
            var value = valueAccessor();
            if (ko.isWriteableObservable(value))
                value(ko.bindingHandlers['selectedOptions'].getSelectedValuesFromSelectNode(this));
            else {
                var allBindings = allBindingsAccessor();
                if (allBindings['_ko_property_writers'] && allBindings['_ko_property_writers']['value'])
                    allBindings['_ko_property_writers']['value'](ko.bindingHandlers['selectedOptions'].getSelectedValuesFromSelectNode(this));
            }
        });
    },
    'update': function (element, valueAccessor) {
        if (element.tagName != "SELECT")
            throw new Error("values binding applies only to SELECT elements");

        var newValue = ko.utils.unwrapObservable(valueAccessor());
        if (newValue && typeof newValue.length == "number") {
            ko.bindingHandlers['selectedOptions'].setSelectedValuesFromSelectNode(element, newValue);
        }
    }
};
})();



<select data-bind="groupedOptions: collection, optionsText: 'Text', optionsValue: 'Value', optionsGroup: 'Group', selectedOptions: Selected"></select>

jsfiddle

source

Pemphigus answered 21/8, 2013 at 6:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.