javascript - KnockoutJS - 将输入值链接到数据列表值

标签 javascript html knockout.js html-datalist

我正在生成一个基于 ko observable 的 datalist 选项。

function Company(company) {
    this.id = company.Id;
    this.name = company.Name;
    this.state = company.State.name;
}


var self = this;
self.companies = ko.observable();
$.get('...', {}, function(result) {

    ko.utils.arrayForEach(result, function(company) {
        self.companies.push(new Company(ko.toJS(company)));
    });
});

HTML:

<input type="text" data-bind="value: selectedCompany" list="companiesList" />
<datalist id="companiesList" data-bind="foreach: companies">
    <option data-bind="value: name, text: state"></option>
</datalist>

好的,事情是这样的:在 Company 函数中,我存储了该公司的 Id。我想做的事?能让我将 Id 值链接到 selectedCompany 变量而不是名称的东西吗?我想显示name属性,但我需要存储Id。

有没有办法做到这一点? 谢谢大家!

最佳答案

I want to display the name property, but I need to store the Id.

由于目前没有针对 HTML5 的 Knockout 绑定(bind) <datalist> , 我做了一个。

我尽可能多地从 select 借钱绑定(bind),所以支持 options , optionsTextoptionsValue .您可以通过 value 指定目标可观察对象.

ko.bindingHandlers.datalist = (function () {
    function getVal(rawItem, prop) {
        var item = ko.unwrap(rawItem);
        return item && prop ? ko.unwrap(item[prop]) : item;
    }

    function findItem(options, prop, ref) {
        return ko.utils.arrayFirst(options, function (item) {
            return ref === getVal(item, prop);
        });
    }
    return {
        init: function (element, valueAccessor, allBindingsAccessor) {
            var setup = valueAccessor(),
                textProperty = ko.unwrap(setup.optionsText),
                valueProperty = ko.unwrap(setup.optionsValue),
                dataItems = ko.unwrap(setup.options),
                myValue = setup.value,
                koValue = allBindingsAccessor().value,
                datalist = document.createElement("DATALIST");

            // create an associated <datalist> element
            datalist.id = element.getAttribute("list");
            document.body.appendChild(datalist);

            // when the value is changed, write to the associated myValue observable
            function onNewValue(newVal) {
                var dataItems = ko.unwrap(setup.options),
                    selectedItem = findItem(dataItems, textProperty, newVal),
                    newValue = selectedItem ? getVal(selectedItem, valueProperty) : void 0;

                if (ko.isWriteableObservable(myValue)) {
                    myValue(newValue);
                }
            }

            // listen for value changes
            // - either via KO's value binding (preferred) or the change event
            if (ko.isSubscribable(koValue)) {
                koValue.subscribe(onNewValue);
            } else {
                ko.utils.registerEventHandler(element, "change", function () {
                    onNewValue(this.value);
                });
            }

            // init the element's value
            // - either via the myValue observable (preferred) or KO's value binding
            if (ko.isObservable(myValue) && myValue()) {
                element.value = getVal(findItem(dataItems, valueProperty, myValue()), textProperty);
            } else if (ko.isObservable(koValue) && koValue()) {
                onNewValue(koValue());
            }
        },
        update: function (element, valueAccessor) {
            var setup = valueAccessor(),
                datalist = element.list,
                dataItems = ko.unwrap(setup.options),
                textProperty = ko.unwrap(setup.optionsText);

            // rebuild list of options when an underlying observable changes
            datalist.innerHTML = "";
            ko.utils.arrayForEach(dataItems, function (item) {
                var option = document.createElement("OPTION");
                option.value = getVal(item, textProperty);
                datalist.appendChild(option);
            });
            ko.utils.triggerEvent(element, "change");
        }
    };
})();

我希望我大部分都答对了。欢迎提出更正和改进建议。

你会像这样使用它:

<input list="company" data-bind="datalist: {
    options: companies, 
    optionsValue: 'id', 
    optionsText: 'name', 
    value: selectedCompany
}" />

注释。

  • 无需创建单独的 <datalist>在 HTML 中。自定义绑定(bind)会为您做到这一点。
  • 但是,您必须提供 list="" <input> 的属性元素。到目前为止,我找不到在 JavaScript 中动态设置它的方法。
  • value是可选的,但如果您提供它,则它必须是可观察的。
  • 您仍然可以使用原来的 valuedatalist 之外绑定(bind).它将包含 <input> 中的任何文本显示(那里没有惊喜)。但是,写入内置值也会更新 datalist值(value),反之亦然。
  • datalist value 具有优先级,并将在 View 模型初始化时覆盖内置值。之后他们保持同步。
  • options应该是对象的数组(普通旧的或可观察的)——在这种情况下是公司)。
  • optionsTextoptionsValue是应映射到选项属性的字符串。
  • 您不必使用 optionsValue — 在这种情况下,整个对象(一家公司)将存储在 value 中.与仅存储 ID 相比,我更愿意这样做。
  • 目前设置对 change 使用react事件。这意味着在您离开输入字段之前,您的 View 模型不会更新。
  • 已在 Chrome 32、YMMV 上测试。正如我所说,欢迎提出意见和更正。

下面是一个demo,复制自原文fiddle .

function main() {
  function Company(company) {
      this.id = company.Id;
      this.name = company.Name;
      this.state = company.State.name;
  }

  function ViewModel(sampleData) {
      var self = this;

      self.companies = ko.observableArray();
      ko.utils.arrayForEach(sampleData, function (companyData) {
          self.companies.push(new Company(companyData));
      });

      // KO's "value" binding _can_ supply a start value
      self.val = ko.observable("Microsoft");

      // ... but it is overridden by datalist's "value" binding - in any case you can use both
      self.selectedCompany = ko.observable(1);
  }

  // -----------------------------------------------------------------------------------

  ko.applyBindings(new ViewModel([{
      Id: 1,
      Name: "Apple",
      State: {
          name: "California"
      }
  }, {
      Id: 2,
      Name: "Microsoft",
      State: {
          name: "Washington"
      }
  }, {
      Id: 3,
      Name: "IBM",
      State: {
          name: "New York"
      }
  }]));
}

// -----------------------------------------------------------------------------------
ko.bindingHandlers.datalist = (function () {
    function getVal(rawItem, prop) {
        var item = ko.unwrap(rawItem);
        return item && prop ? ko.unwrap(item[prop]) : item;
    }

    function findItem(options, prop, ref) {
        return ko.utils.arrayFirst(options, function (item) {
            return ref === getVal(item, prop);
        });
    }
    return {
        init: function (element, valueAccessor, allBindingsAccessor) {
            var setup = valueAccessor(),
                textProperty = ko.unwrap(setup.optionsText),
                valueProperty = ko.unwrap(setup.optionsValue),
                dataItems = ko.unwrap(setup.options),
                myValue = setup.value,
                koValue = allBindingsAccessor().value,
                datalist = document.createElement("DATALIST");

            // create an associated <datalist> element
            datalist.id = element.getAttribute("list");
            document.body.appendChild(datalist);

            // when the value is changed, write to the associated myValue observable
            function onNewValue(newVal) {
                var dataItems = ko.unwrap(setup.options),
                    selectedItem = findItem(dataItems, textProperty, newVal),
                    newValue = selectedItem ? getVal(selectedItem, valueProperty) : void 0;

                if (ko.isWriteableObservable(myValue)) {
                    myValue(newValue);
                }
            }

            // listen for value changes
            // - either via KO's value binding (preferred) or the change event
            if (ko.isSubscribable(koValue)) {
                koValue.subscribe(onNewValue);
            } else {
                ko.utils.registerEventHandler(element, "change", function () {
                    onNewValue(this.value);
                });
            }

            // init the element's value
            // - either via the myValue observable (preferred) or KO's value binding
            if (ko.isObservable(myValue) && myValue()) {
                element.value = getVal(findItem(dataItems, valueProperty, myValue()), textProperty);
            } else if (ko.isObservable(koValue) && koValue()) {
                onNewValue(koValue());
            }
        },
        update: function (element, valueAccessor) {
            var setup = valueAccessor(),
                datalist = element.list,
                dataItems = ko.unwrap(setup.options),
                textProperty = ko.unwrap(setup.optionsText);

            // rebuild list of options when an underlying observable changes
            datalist.innerHTML = "";
            ko.utils.arrayForEach(dataItems, function (item) {
                var option = document.createElement("OPTION");
                option.value = getVal(item, textProperty);
                datalist.appendChild(option);
            });
            ko.utils.triggerEvent(element, "change");
        }
    };
})();
main();
.hint {
    color: silver;
    font-size: 80%;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.0.0/knockout-min.js"></script>
<input list="company" type="text" placeholder="Select a company..." data-bind="value: val, datalist: {
    options: companies, 
    optionsValue: 'id',  /* try removing 'optionsValue' and see what happens to the view model */
    optionsText: 'name', 
    value: selectedCompany
}" />
<span class="hint">(note the "change" event occurs after the field loses focus!)</span>

<hr />
<p>View Model:</p>
<pre data-bind="text: ko.toJSON($root, null, 2)"></pre>

关于javascript - KnockoutJS - 将输入值链接到数据列表值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19865364/

相关文章:

javascript - 如何将扩展器添加到 Knockout 中的现有可观察对象

javascript - 使用 knockout 将图像源与文件输入绑定(bind)

javascript - 如何在 jQuery 中通过类名选择子元素?

javascript - Mongoose.model ('model_name' ) 当 mongoose 模型模式文件中需要函数时返回空对象

javascript - JQuery 一次切换两个类

html - 如何将多个分区修复到屏幕顶部(视差滚动网站帮助)

javascript - knockout 'flickering' 问题

javascript - 'el' 在 vi​​ew.render().el 中做了什么?

javascript - 从 JQuery 方法将值设置为 TextField

JavaScript - 根据事件在同一区域显示不同的字段集