2012-07-30 48 views
1

我已经实现了其中的灵感来自于一个knockout.js分组选择框中选择(我想通了通过查看)通过RP尼迈耶 KnockoutJS - Binding value of select with optgroup and javascript objects 但它是一个有点不同。它看起来像这样。Knockout.js OPTGROUP与optioncaption

<select name="Field" data-bind="foreach: FieldList.Groups, value:Field" > 
    <optgroup data-bind="attr: {label: Label}, foreach: Children"> 
     <option data-bind="text: Text, value: Value"></option> 
    </optgroup> 
</select> 

我的视图模型会是什么样子

var viewmodel = { 
    Field: 2, 
    Groups:{ 
     Label:"Field 1", 
     Children:[ 
     { Text:"Field1", Value:1 }, 
     { Text:"Field2", Value:2 } 
     ] 
    } 

类似的东西,在任何情况下,它的伟大工程。不过,我真的需要把“请选择......”作为第一个选项。

鉴于这是一个foreach循环

一)optionsCaption约束力不工作,

B)我不能添加选项在那里cuz它会被重复每个组。

只是为了确保没有人可以帮助我,我会添加这个约束。我实际上是代码gen'ing(只是服务器上的一些C#)的HTML,虽然我可以做很多自定义的东西,我不能添加任意的文本片段,即注释输出。这就是说我不能做无容器的foreach,因为我只能提交htmltags而不是html注释。

ugg。

如果有人有任何想法请让我知道,我将不胜感激。 谢谢, R

回答

4

好吧,所以我决定只是做我自己的约束力。然后,我比预期容易得多。我会在这里发布。我只是从源代码中直接剥离了这一点,并稍微修改了它。这些变化真的很简单,我强烈建议你比较两个,看看我在做什么,现在有一些注意事项:

a)为了我自己的方便,我已经基本硬编码了一些“约定”基本上你应该看看我使用的viewmodel,如果你不能/不想重现类似的东西,你可能不得不改变代码以反映这一点。但它是死的简单(与萤火虫:))

b)它正在更新模型的选择值为单选择,但我没有尝试过使用多选。我正在使用其他的东西来进行多重选择。但是我从原始的“选项”绑定中留下了该代码。

---更新---

想出℃。它可以更新视图模型。

----结束更新---

三)我不知道,如果你改变模型,可以将更新的选择。坦率地说,当我意识到自己似乎无法在常规选择框中更新选择选项时,我正在进行测试,所以我对此感到困惑。如果我弄清楚,我会更新。

d)您必须包含ensureDropdownSelectionIsConsistentWithModelValue函数,因为它是ko中的“私人”函数,您无法从外面找到它。

function ensureDropdownSelectionIsConsistentWithModelValue(element, modelValue, preferModelValue) { 
    if (preferModelValue) { 
     if (modelValue !== ko.selectExtensions.readValue(element)) 
      ko.selectExtensions.writeValue(element, modelValue); 
    } 

    // No matter which direction we're syncing in, we want the end result to be equality between dropdown value and model value. 
    // If they aren't equal, either we prefer the dropdown value, or the model value couldn't be represented, so either way, 
    // change the model value to match the dropdown. 
    if (modelValue !== ko.selectExtensions.readValue(element)) 
     ko.utils.triggerEvent(element, "change"); 
} 

ko.bindingHandlers['groupedSelect'] = { 
'update': function (element, valueAccessor, allBindingsAccessor) { 
    if (ko.utils.tagNameLower(element) !== "select") 
     throw new Error("options binding applies only to SELECT elements"); 

    var selectWasPreviouslyEmpty = element.length == 0; 
    var previousSelectedValues = ko.utils.arrayMap(ko.utils.arrayFilter(element.childNodes, function (node) { 
     return node.tagName && (ko.utils.tagNameLower(node) === "option") && node.selected; 
    }), function (node) { 
     return ko.selectExtensions.readValue(node) || node.innerText || node.textContent; 
    }); 
    var previousScrollTop = element.scrollTop; 

    var value = ko.utils.unwrapObservable(valueAccessor()); 
    value = value.groups(); 
    var selectedValue = element.value; 

    // Remove all existing <option>s. 
    // Need to use .remove() rather than .removeChild() for <option>s otherwise IE behaves oddly (https://github.com/SteveSanderson/knockout/issues/134) 
    while (element.length > 0) { 
     ko.cleanNode(element.options[0]); 
     element.remove(0); 
    } 

    if (value) { 
     var allBindings = allBindingsAccessor(); 
     if (typeof value.length != "number") 
      value = [value]; 
     if (allBindings['optionsCaption']) { 
      var option = document.createElement("option"); 
      ko.utils.setHtml(option, allBindings['optionsCaption']); 
      ko.selectExtensions.writeValue(option, undefined); 
      element.appendChild(option); 
     } 
     for (var a= 0, b = value.length; a < b; a++) { 
      var optGroup = document.createElement("optgroup"); 
      ko.bindingHandlers['attr'].update(optGroup, ko.observable({label: value[a].label()})); 
      var children = ko.utils.unwrapObservable(value[a].children()); 
      for (c=0, d=children.length; c<d; c++){ 
       var option = document.createElement("option"); 

       // Apply a value to the option element 
       var optionValue = typeof allBindings['optionsValue'] == "string" ? value[a].children()[c][allBindings['optionsValue']] : value[a].children()[c]; 
       optionValue = ko.utils.unwrapObservable(optionValue); 
       ko.selectExtensions.writeValue(option, optionValue); 

       // Apply some text to the option element 
       var optionsTextValue = allBindings['optionsText']; 
       var optionText; 
       if (typeof optionsTextValue == "function") 
        optionText = optionsTextValue(value[a].children()[c]); // Given a function; run it against the data value 
       else if (typeof optionsTextValue == "string") 
        optionText = value[a].children()[c][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 
       if ((optionText === null) || (optionText === undefined)) 
        optionText = ""; 

       ko.utils.setTextContent(option, optionText); 
       optGroup.appendChild(option); 
      } 
      element.appendChild(optGroup); 
     } 

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

     element.scrollTop = previousScrollTop; 

     if (selectWasPreviouslyEmpty && ('value' in allBindings)) { 
      // Ensure consistency between model value and selected option. 
      // If the dropdown is being populated for the first time here (or was otherwise previously empty), 
      // the dropdown selection state is meaningless, so we preserve the model value. 
      ensureDropdownSelectionIsConsistentWithModelValue(element, ko.utils.unwrapObservable(allBindings['value']), /* preferModelValue */ true); 
     } 

     // Workaround for IE9 bug 
     ko.utils.ensureSelectElementIsRenderedCorrectly(element); 
    } 
} 

使用

<select data-bind="groupedSelect:FieldList,optionsText:'Text',optionsValue:'Value',optionsCaption:'-- Please Select --',value:FieldEntityId"> 

视图模型

"FieldList":{ 
    "groups":[ 
     {"label":"SomeGroup1","children":[ 
      {"Text":"field1","Value":"1"}, 
      {"Text":"field2","Value":"2"} 
     ]}, 
     {"label":"SomeGroup 2","children":[ 
      {"Text":"field3","Value":"3"}, 
      {"Text":"field4","Value":"4"} 
     ]} 
    ] 
} 

我想如果你有任何问题或意见,让我知道。
也让我解释为什么有模型看起来,好吧,有点奇怪。我真的只是从C#模型,看起来像这样

public class GroupSelectViewModel 
{ 
    public GroupSelectViewModel() 
    { 
     groups = new List<SelectGroup>(); 
    } 

    public List<SelectGroup> groups { get; set; } 
} 
public class SelectGroup 
{ 
    public string label { get; set; } 
    public IEnumerable<SelectListItem> children { get; set; } 
} 

SelecListItem是使用帕斯卡表示法的C#类序列化。

+0

嗨,你能生产一个jsfiddle吗?我尝试,但没有运气。谢谢。或者你是否找到了另一种实现它的方法? – Bronzato 2013-04-12 18:59:23