2012-01-23 7 views
18

Czy jest jakiś sposób w powiązaniu Knockoutjs, gdzie mogę określić optionsGroup? coś podobnego do wypełniania:Knockoutjs wybierz z grupą opcji

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

Proszę o odpowiedź.

+0

Zgaduję, że można użyć wiązania attr, jak udokumentowano na http://knockoutjs.com/documentation/attr-binding.html – kfuglsang

+5

Tutaj jest wątek o dodawaniu opcjiGrupowe wsparcie: https://github.com/SteveSanderson/knockout/pull/94 –

Odpowiedz

30

mam odpowiedzi na to samo, tu jest odpowiedź, jeśli ktoś chce,

<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> 
+0

czy mógłbyś pokazać nam część javascript? Jestem naprawdę zainteresowany!! Dzięki – jbartolome

0

Spróbuj tego.

(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

2

W wielu przypadkach nie trzeba opcje się być obserwowalne, tylko wynik wyboru. Oto przykład, który wybiera hrabstwo w Wielkiej Brytanii.

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; 
} 

Będziesz pobrać wybraną opcję w viewModel.countyList.

1

Pozwoli również zastępczy:

<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 byłoby w tym formacie:

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; 
}) 

wartość Ta opcja może być wartość w ten prosty przykład.

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

Ale dla bardziej złożonego obiektu lepszym rozwiązaniem byłyby dane w formacie $.