好吧,所以我決定只是做我自己的約束力。然後,我比預期容易得多。我會在這裏發佈。我只是從源代碼中直接剝離了這一點,並稍微修改了它。這些變化真的很簡單,我強烈建議你比較兩個,看看我在做什麼,現在有一些注意事項:
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#類序列化。
嗨,你能生產一個jsfiddle嗎?我嘗試,但沒有運氣。謝謝。或者你是否找到了另一種實現它的方法? – Bronzato 2013-04-12 18:59:23