1

KnockoutJS - RP Niemeyer による optgroup および javascript オブジェクトによる select の値のバインドに触発された (私はそれを見て理解しました) ノックアウト js にグループ化された選択ボックスを実装しました が、少し異なります。このように見えます。

<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ループであることを考えると

a) optionsCaption バインディングが機能せず、

b)グループごとに繰り返されるため、そこにオプションを追加することはできません。

誰も私を助けられないようにするために、この制約を追加します。私は実際にhtmlをコード化しています(サーバー上のC#だけ)。多くのカスタムを行うことができますが、任意のテキスト、つまり出力にコメントを追加することはできません。つまり、html コメントではなく html タグしか発行できないため、コンテナレスの foreach を実行することはできません。

ウグ。

誰かが何か考えを持っているなら、私に知らせてください。ありがとう、R

4

1 に答える 1

4

よし、自分でバインディングすることにした。思ったよりずっと簡単でした。ここに投稿します。これをソースコードから直接取り出して、わずかに変更しました。変更は非常に単純です。2 つを比較して、私が行っていることを確認することを強くお勧めします。注意点がいくつかあります。

a)基本的に、自分の便宜のためにいくつかの「規則」をハードコーディングしました。これは、どの型モデルに対しても十分に一般的なものにする方法がよくわからなかったためです。基本的に、私が使用しているビューモデルを確認する必要があります。同様のものを再現できない/再現したくない場合は、おそらくそれを反映するようにコードを変更する必要があります.しかし、それは非常に単純です(firebugを使用:))

b)単一選択のモデルの選択値を更新していますが、複数選択を使用してこれを試したことはありません。複数選択に別のものを使用しています。しかし、元の「オプション」バインディングからそのコードを残しました。

- -アップデート - -

考え出した c. ビューモデルを更新するために機能します。

----更新終了----

c) モデルを変更すると、選択が更新されるかどうかわかりません。率直に言って、通常の選択ボックスの選択オプションを更新できないように見えることに気付いたとき、私はそれをテストしていたので、少し困惑しています。わかり次第更新します。

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 は、パスカル表記を使用する ac# クラスです。

于 2012-07-31T14:57:47.993 に答える